I was playing around with polymorphism in C++, and I found out about std::variant. However it doesn't seem very performant nor coding friendly, and in most cases you have to use virtual base class anyways.
So I "reinvented" the tagged union, that allows you to store multiple classes and allows you to call their member functions almost directly depending on the stored type.
Consider that I have the following classes containing methods with the same signature:
struct A {
void print() { printf("A"); }
char id() { return 'A'; }
};
struct B {
void print() { printf("B"); }
char id() { return 'B'; }
};
struct C {
void print() { printf("C"); }
char id() { return 'C'; }
};
struct D {
void print() { printf("D"); }
char id() { return 'D'; }
};
Now I have a tagged_union class, that contains the union of all the classes along with stored type id, and implements their methods using internal switch (not the real use case, just an example, I really use it in a raytracer for storing different primitive meshes):
struct tagged_union{
tagged_union(){}
tagged_union(A val): a(val), type(0) {}
tagged_union(B val): b(val), type(1) {}
tagged_union(C val): c(val), type(2) {}
tagged_union(D val): d(val), type(3) {}
void print() {
switch(type){
case 0: a.print(); break;
case 1: b.print(); break;
case 2: c.print(); break;
case 3: d.print(); break;
default: break;
}
}
char id() {
switch(type){
case 0: return a.id(); break;
case 1: return b.id(); break;
case 2: return c.id(); break;
case 3: return d.id(); break;
default: return 0;
}
}
private:
union{
A a;
B b;
C c;
D d;
};
int type;
};
Now my question is: is it possible to automate this process of defining the union (eg. variadic templates + recursively defining the members), and selecting the correct stored type in member functions at runtime, without having to write out switch for each member function ?
I apologize for any grammar errors, and incorrect terminology, as I'm not a native speaker.
Edit: It was clarified to me, that you don't have to use virtual in order to call polymorphics functions from variant.However I still stand by the original question out of sheer curiosity.
You can create template method to do the dispatch once (or twice for const
version):
template <typename F>
decltype(auto) call(F f) const
{
switch (type){
case 0: return f(a);
case 1: return f(b);
case 2: return f(c);
case 3: return f(d);
}
throw std::runtime_error ("Unreachable code");
}
void print() const {
call([](const auto& u){ u.print(); });
}
char id() const {
return call([](const auto& u){ return u.id(); });
}