I am new to variadic templates and packed arguments and all. I want to have a "entity component system" in my program, and while trying to add component to the entity, I came to realize that my attempt has a major flaw.
Anyways here is my failed attempt.
struct A{
float x;
float y;
A(float _x, float _y):x(_x), y(_y){}
};
struct B{
float x;
float y;
float z;
B(float _x, float _y, float _z):x(_x), y(_y), z(_z){}
};
struct Entity{
A* a = NULL;
B* b = NULL;
Entity(){}
template<typename T, typename... args>
void Add(args... _args){
if(typeid(T) == typeid(A))
a = new A(_args...);
else if(typeid(T) == typeid(B))
b = new B(_args...);
else
throw("Invalid component added");
}
};
And implementation looks like this..
Entity ent;
ent.Add<A>(12.24f, 123.246f);
ent.Add<B>(1.f, 1.2f, 1.23f);
I want the implementation to work somehow.. what has to be changed for it??
You can use the following (all in compile time):
std::is_same
to check if 2 types are the same (requires #include <type_traits>
).if constexpr
to branch based on it (c++17 onwards).static_assert
if you want to get a compilation error if Add
is instatiated with types other than A
or B
(see note about it below).Your modified code:
#include <type_traits>
struct A {
float x;
float y;
A(float _x, float _y) :x(_x), y(_y) {}
};
struct B {
float x;
float y;
float z;
B(float _x, float _y, float _z) :x(_x), y(_y), z(_z) {}
};
struct Entity {
A* a = nullptr;
B* b = nullptr;
Entity() {}
template<typename T, typename... args>
void Add(args... _args) {
if constexpr (std::is_same_v<T, A>)
a = new A(_args...);
else if constexpr (std::is_same_v<T, B>)
b = new B(_args...);
else
//static_assert(!sizeof(T), "T must be A or B"); // Enable this line to get a compilation error in this case
throw("Invalid component added");
}
};
int main() {
Entity ent;
ent.Add<A>(12.24f, 123.246f);
ent.Add<B>(1.f, 1.2f, 1.23f);
}
Notes:
nullptr
which is recommended instead of NULL
.static_assert(false, ...)
is not guaranteed to work for the reason mentioned in the article in @RemyLebeau's comment (How can I create a type-dependent expression that is always false?).static_assert(!sizeof(T), ...)
assuming sizeof is never 0. The article also suggests a slightly more complicated solution that avoids this assumption.