Search code examples
c++templatescontainersheterogeneous-array

c++ heterogeneous container, get entry as type


I have the following simple implementation of a Heterogeneous container:

struct Container {
    struct HolderBase {
    };

    template<typename S>
    struct Holder : HolderBase {
        Holder(S* s) : s_(s) {}
        S* s_;
    };

    template<typename S>
    void push_back(S* s) {
        h_.push_back(new Holder<S>(s));
    }

    vector<HolderBase*> h_;

    template<typename B>
    B* get(int i) {
        //magic here
    }
};

Here's how to use it:

struct ElementBase {};
struct Element : ElementBase {};

int main()
{
    Container container;
    container.push_back(new Element);
    ElementBase* elementBase = container.get<ElementBase>(0);
}

I can add entries of any type to it. But I can't figure out how to implement a function to retrieve elements, as some type, which may be the same as the entry or a base class to it.

What I need seems to be both virtual and template at the same time, which is not possible.


Solution

  • It doesn't seem possible to have exactly what you want without much pain and inconvenience (for example, registering all classes you want to work with in some kind of central repository).

    Here's one way to do almost what you want that can perhaps be useful.

    class HolderBase
    {
      public:
        virtual ~HolderBase() = default;    
        template <class X> X* get() { return dynamic_cast<X*>(this); }
    };
    
    template <class T>
    class Holder : public HolderBase, public T
    {
      public:
        using T::T;
    };
    

    Your container is then just a vector<unique_ptr<HolderBase>> or whatever bunch-of-pointers you fancy.

    Test drive:

    struct A {
        virtual ~A() = default;
        A(int a) : a(a) {};
        int a;
    };
    
    struct B : A {
        B(int a, int b) : A(a), b(b) {};
        int b;
    };
    
    struct C : A {
        C(int a, int c) : A(a), c(c) {};
        int c;
    };
    
    
    int main () {
        std::vector<std::unique_ptr<HolderBase>> v;
        v.emplace_back(std::make_unique<Holder<B>>(7,40));
        v.emplace_back(std::make_unique<Holder<C>>(0,42));
    
        A* a = v[0]->template get<A>();
        B* b = v[0]->template get<B>();
        C* c = v[0]->template get<C>();
    
        std::cout << a << " " << b << " " << c << "\n";
    
        a = v[1]->template get<A>();
        b = v[1]->template get<B>();
        c = v[1]->template get<C>();
    
        std::cout << a << " " << b << " " << c << "\n";
    }