albinopapa wrote:I guess what you need to ask yourself is, what's the goal?
The goal at the beginning is simple: have 1000 instances of one of 56 different objects be stored in memory without the ridiculousness of having 56 arrays. I am also trying to avoid a "one struct for all" solution since that has a lot of problems too especially since it would basically need to juggle the functionality of 56 different objects and you don't know which variable is used for which. Emulating how a union handles data and making them be able to share the same memory one at a time seems to be the most efficient solution to storing these objects in memory. Any other solution would either waste way too much memory or be really inefficient in comparison(
50 times faster!).
A second goal that was created after I realise what a class like this can do in terms of type switching. I have grown weary of the limitations of polymorphism. Say that I have a base class called Shape. I want this class to calculate it's area and save it as any type I specify it to.
"Template functions cannot be virtual!"
Oh, I'll use double by default then. Now I have a nice class called SquareRenderer and I want it to try and render my shape if it is a square.
"You cannot reliably downcast with the tools available since dynamic_cast is so horribly implemented and supported."
I've been moving away from polymorphism because of this and have been seeking alternatives ever since. The Room class gives me a way to reveal it's stored type with optional type safety checks implemented which would make it a superior way to implement type switching if it wasn't for the fact that you need to type the switch() statement manually instead of having the compiler do it for you.
albinopapa wrote:
If you are trying to optimize your program or worried about speed, then be specific in your code. Having a class that does general things is never going to be as fast as a class that does a specific task.
That would be insane. These classes are mostly disposable and would change every time I make a new project or try to make updates to existing ones. Having them line up in memory is already enough of a gain for me to strive for anyway(
50 times faster!!!).
cameron wrote:I would be interested in seeing the rest of the implementation. I am curious about the implementation of EnumClass, GetCurrentOccupant and how you store the data type currently occupying the obj.
I tried to avoid having to show people my code too much since it uses a lot of template metaprogramming techniques that have a bad reputation for being hard to read. Most people are not familiar with them and even veterans of C++ avoid them because they hate their syntax(
Google has practically banned them from their standard!)
TypeList.h
Code: Select all
#ifndef SHARED_TYPE_LIST_H_
#define SHARED_TYPE_LIST_H_
#include <utility>
namespace shared
{
template<class FirstType, class SecondType, class...OtherTypes>
//used to provide compile time information. Will never actually be compiled into final program.
class TypeList
{
private:
template<class Type>
//an alias for convienient testing for void
using IsVoid = std::is_same<Type, void>;
template<class TestType, class TypeToCheckWith, class...OtherTypes>
//check for duplicates on an individual type
struct CheckTypeForDuplicates
{
enum
{
value = std::is_same<TestType, TypeToCheckWith>::value || CheckTypeForDuplicates<TestType, OtherTypes...>::value
};
};
template<class TestType, class TypeToCheckWith>
//recursion termination
struct CheckTypeForDuplicates<TestType, TypeToCheckWith>
{
enum
{
value = std::is_same<TestType, TypeToCheckWith>::value
};
};
template<class FirstType, class SecondType, class...OtherTypes>
//check for duplicates on all given types
struct CheckListForDuplicates
{
enum
{
value = CheckTypeForDuplicates<FirstType, SecondType, OtherTypes...>::value ||
CheckListForDuplicates<SecondType, OtherTypes...>::value
};
};
template<class SecondLastType, class LastType>
//recursion termination
struct CheckListForDuplicates<SecondLastType, LastType>
{
enum
{
value = CheckTypeForDuplicates<FirstType, SecondType>::value
};
};
template<class FirstType, class...OtherTypes>
//check for void types within a list of types
struct CheckForVoid
{
enum
{
value = IsVoid<FirstType>::value || CheckForVoid<OtherTypes...>::value
};
};
template<class LastType>
//recursion termination
struct CheckForVoid<LastType>
{
enum
{
value = IsVoid<LastType>::value
};
};
template<class FirstType, class...Types>
//represent an element in a list and used to create other elements in the list
struct TypeListElement
{
using Type = FirstType;
using Next = TypeListElement<Types...>;
};
template<class LastType>
//recursion termination. Any other type after this will always be void.
struct TypeListElement<LastType>
{
using Type = LastType;
using Next = TypeListElement<void>;
};
template<class List, std::size_t index>
//get element by index by counting down while going through the list. Will be void if we go out of scope.
struct GetElementByIndex
{
//typename nessasary to declare member as type and not as value(because it's not like the program can just infer that it is or anything. Urgh...)
using Element = typename GetElementByIndex<typename List::Next, index - std::size_t(1)>::Element;
};
template<class LastElement>
//recursion termination.
struct GetElementByIndex<LastElement, 1>
{
using Element = LastElement;
};
template<class List>
//list will always have void as the first element
struct GetElementByIndex<List, 0>
{
using Element = TypeListElement<void>;
};
template<class List, class CurrentType, class FindType, std::size_t current_index>
//get the index of a type by finding a matching type in the list and counting the index.
struct GetIndexOfElement
{
enum
{
index = GetIndexOfElement<typename List::Next, typename List::Type, FindType, current_index + std::size_t(1)>::index
};
};
template<class List, class FoundType, std::size_t current_index>
//when a matching type is found
struct GetIndexOfElement<List, FoundType, FoundType, current_index>
{
enum
{
index = current_index
};
};
template<class List, class TypeNotFound, std::size_t current_index>
//when we reach the end of the list
struct GetIndexOfElement<List, void, TypeNotFound, current_index>
{
enum
{
index = -1
};
};
template<class List, class CurrentType, std::size_t current_index>
//when void is searched for
struct GetIndexOfElement<List, CurrentType, void, current_index>
{
enum
{
index = 0
};
};
//an alias for convienient access to the list
using List = TypeListElement<FirstType, SecondType, OtherTypes...>;
public:
//size = number of other types plus the first 2 types and void.
enum
{
size = sizeof...(OtherTypes) + 2 + 1
};
template<std::size_t index>
//alias for searching type by index
using TypeAt = typename GetElementByIndex<List, index>::Element::Type;
template<class Type>
//alias for finding index of type
using IndexOf = GetIndexOfElement<typename List::Next, typename List::Type, Type, 1>;
//alias for checking for duplicates in the list
using HasDuplicates = CheckListForDuplicates<FirstType, SecondType, OtherTypes...>;
//alias for checking for void in the given types. List will always reserve the 0th member for void so there is no need for the user to give one.
using HasVoid = CheckForVoid<FirstType, SecondType, OtherTypes...>;
};
}
#endif
EnumClass.h
Code: Select all
#ifndef SHARED_ENUM_CLASS_H_
#define SHARED_ENUM_CLASS_H_
#include "TypeList.h"
namespace shared
{
template<class Num, class FirstType, class SecondType, class...OtherTypes>
class EnumClass
{
private:
using List = TypeList<FirstType, SecondType, OtherTypes...>;
static_assert(!List::HasDuplicates::value, "Cannot have duplicate Types!");
static_assert(!List::HasVoid::value, "Cannot have void Types!");
public:
template<std::size_t index>
using TypeAt = typename TypeList<FirstType, SecondType, OtherTypes...>::TypeAt<index>;
template<class Type>
using IndexOf = typename List::IndexOf<Type>;
private:
template<class Num, class FirstType, class SecondType, class...OtherTypes>
//all versions of enum class is friends with every other version
friend class EnumClass;
Num enum_;
template<class OtherList>
//creates a table to convert an index from another list to our list
struct ConversionTable
{
private:
Num conversion[OtherList::size];
//set the conversion table
template<class OtherList, std::size_t index, std::size_t TOTAL>
struct SetTable
{
static void Set(Num*const conversion)
{
*conversion = IndexOf<typename OtherList::TypeAt<index>>::index;
SetTable<OtherList, index + std::size_t(1), TOTAL>::Set(conversion + 1);
}
};
//recursion termination
template<class OtherList, std::size_t TOTAL>
struct SetTable<OtherList, TOTAL, TOTAL>
{
static void Set(Num*const conversion)
{
}
};
public:
ConversionTable()
{
SetTable<OtherList, 0, OtherList::size>::Set(conversion);
}
Num Convert(const Num& index) const
{
return conversion[index];
}
};
public:
EnumClass()
:
enum_(0)
{
}
template<class FirstType, class SecondType, class...OtherTypes>
EnumClass(const EnumClass<Num, FirstType, SecondType, OtherTypes...>& right_hand_side)
:
enum_(0)
{
//just use the table in the copy operator overload function to not waste memory.
*this = right_hand_side;
}
template<class Type>
Num Set()
{
static_assert(IndexOf<Type>::index != -1, "Error: Type does not exist within the TypeList!");
enum_ = IndexOf<Type>::index;
return enum_;
}
Num GetEnum() const
{
return enum_;
}
template<class FirstType, class SecondType, class...OtherTypes>
EnumClass& operator=(const EnumClass<Num, FirstType, SecondType, OtherTypes...>& right_hand_side)
{
using OtherList = EnumClass<Num, FirstType, SecondType, OtherTypes...>::List;
//one conversion table for every conversion made.
static const ConversionTable<OtherList> conversion_table;
enum_ = conversion_table.Convert(right_hand_side.enum_);
return *this;
}
};
}
#endif
Room.h
Code: Select all
#ifndef SHARED_ROOM_H_
#define SHARED_ROOM_H_
#include <cstdint>
#include <exception>
#include "EnumClass.h"
namespace shared
{
template<class Num, class FirstType, class SecondType, class...OtherTypes>
class Room
{
public:
using EnumClass = EnumClass<Num, FirstType, SecondType, OtherTypes...>;
class Occupied : public std::exception
{
public:
const char* what() const override
{
return "Room is already occupied(You should knock first next time!)";
}
};
class WrongType : public std::exception
{
public:
const char* what() const override
{
return "The type you are looking for is occupying another castle...err...room...";
}
};
private:
template<bool OverrideIfOccupied>
struct DoOverrideIfOccupied
{
template<class Type>
static void Override(Type& current_data, const Type& override_data, EnumClass& enum_class)
{
current_data = override_data;
enum_class.Set<Type>();
}
template<class Type>
//another for move semantics
static void Override(Type& current_data, const Type&& override_data, EnumClass& enum_class)
{
current_data = override_data;
enum_class.Set<Type>();
}
};
template<>
struct DoOverrideIfOccupied<false>
{
template<class Type>
static void Override(Type& current_data, const Type& override_data, EnumClass& enum_class)
{
//if the room is occupied(not occupied by void)
if (enum_class.GetEnum())
{
throw Occupied();
}
else
{
current_data = override_data;
enum_class.Set<Type>();
}
}
template<class Type>
//another for move semantics
static void Override(Type& current_data, const Type&& override_data, EnumClass& enum_class)
{
//if the room is occupied(not occupied by void)
if (enum_class.GetEnum())
{
throw Occupied();
}
else
{
current_data = override_data;
enum_class.Set<Type>();
}
}
};
template<class Type, bool CheckIfOccupying>
struct DoCheckIfOccupying
{
static void Do(const EnumClass& enum_class)
{
//if the room is not being occupied by the given type
if (enum_class.GetEnum() != EnumClass::IndexOf<Type>::index)
{
throw WrongType();
}
}
};
template<class Type>
struct DoCheckIfOccupying<Type, false>
{
//will be optimised away
static void Do(const EnumClass& enum_class)
{
}
};
public:
Num GetCurrentOccupant() const
{
return enum_class_.GetEnum();
}
template<class Type, bool OverrideIfOccupied = false>
void CheckIn(const Type& data)
{
DoOverrideIfOccupied<OverrideIfOccupied>::Override(GetData<Type>(), data, enum_class_);
}
template<class Type, bool OverrideIfOccupied = false>
//another for move semantics
void CheckIn(const Type&& data)
{
DoOverrideIfOccupied<OverrideIfOccupied>::Override(GetData<Type>(), data, enum_class_);
}
template<class Type, bool CheckIfOccupying = true>
Type& Visit()
{
DoCheckIfOccupying<Type, CheckIfOccupying>::Do(enum_class_);
return GetData<Type>();
}
template<class Type, bool CheckIfOccupying = true>
const Type& Visit() const
{
DoCheckIfOccupying<Type, CheckIfOccupying>::Do(enum_class_);
return GetData<Type>();
}
private:
template<class FirstType, class SecondType, bool FirstTypeIsBigger>
struct GetBiggestType
{
using Type = FirstType;
};
template<class FirstType, class SecondType>
struct GetBiggestType<FirstType, SecondType, false>
{
using Type = SecondType;
};
template<class FirstType, class SecondType, class...OtherTypes>
struct FindBiggestType
{
using Type = typename FindBiggestType<typename GetBiggestType<FirstType, SecondType, (sizeof(FirstType) > sizeof(SecondType))>::Type, OtherTypes...>::Type;
};
template<class SecondLastType, class LastType>
struct FindBiggestType<SecondLastType, LastType>
{
using Type = typename GetBiggestType<SecondLastType, LastType, (sizeof(SecondLastType) > sizeof(LastType))>::Type;
};
enum
{
size = sizeof(FindBiggestType<FirstType, SecondType, OtherTypes...>::Type)
};
template<class Type>
Type& GetData()
{
static_assert(EnumClass::IndexOf<Type>::index != -1, "Error: Type does not exist within the TypeList!");
return *((Type*)data_);
}
template<class Type>
const Type& GetData() const
{
static_assert(EnumClass::IndexOf<Type>::index != -1, "Error: Type does not exist within the TypeList!");
return *((Type*)data_);
}
//disable copying once the room is created to force the program to use the safer CheckIn() function for copying.
Room& operator=(const Room& room)
{
throw;
return *this;
}
std::uint8_t data_[size];
EnumClass enum_class_;
};
}
#endif
I did moderate amounts of testing on these classes but it was never extensive. Try them out at your own risk. Also, they were coded in VS2013. Try not to use older versions of visual studio as they may not support the techniques used here.
-Zekilk