Search code examples
c++polymorphismunions

C++ polymorphism using tagged unions


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.


Solution

  • 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(); });
    }
    

    Demo