Search code examples
c++arrayspointerstuplesmetaprogramming

Any way to build an array of pointers from a tuple of different objects (but derived from the same base class)?


Good morning all!

reading stack overflow for a long time, but this is my first post here.

For some reasons I would like to do something like this:

class Base{
   ...
}

class A : public Base{
   ...
}

class B : public Base{
   ...
}

std::tuple<A, B> myTuple{A{}, B{}};

std::array<Base*, 2> myArray{...};

Briefly - I want to store dozens of objects in a tuple object, but at some point I need a container of pointers to all of those elements. All of them inherits from the same class which is my interface class.

And I don't know how to extract elements from myTuple, take pointers to all its elements (in exact the same sequence as it is defined) and assign them to myArray.

I have seen some solutions in different questions, but none of it fits to my specific case. I am just learning templates, so this is kinda difficult for me.

Code taken from: https://stackoverflow.com/a/59561746/19163017

template <class Tuple>
struct make_array;

template <class V, template <V N> class C, V... Ns>
struct make_array<std::tuple<C<Ns>... >> {
    static constexpr Tf::TaskFlow<3>::TaskArray value{&Ns... };
};

template <class Tuple>
constexpr auto make_array_v = make_array<Tuple>::value;

But maybe it could be modified for my needs?


Solution

  • It is possible to create an array of pointers to tuple members using various compile time programming techniques. Below is one implementation. There may be a less verbose way of doing this, but I am no expert on this kind of stuff:

    #include <iostream>
    #include <string>
    #include <array>
    #include <tuple>
    
    class Base {
    public:
        virtual std::string foo() = 0;
    };
    
    class A : public Base {
    public:
        std::string foo() override {
            return "A";
        }
    };
    
    class B : public Base {
    public:
        std::string foo() override {
            return "B";
        }
    };
    
    template<size_t I, typename... Ts>
    void tuple_to_base_ptr_array_aux(std::tuple<Ts...>& tup,
            std::array<Base*, sizeof...(Ts)>& ary) {
        if constexpr (I < sizeof...(Ts)) {
            ary[I] = static_cast<Base*>( &(std::get<I>(tup)) );
            tuple_to_base_ptr_array_aux<I + 1>(tup, ary);
        }
    }
    
    template<typename... Ts>
    std::array<Base*, sizeof...(Ts)> tuple_to_base_ptr_array( std::tuple<Ts...>& tup) {
        std::array<Base*, sizeof...(Ts)> ary;
        tuple_to_base_ptr_array_aux<0>(tup, ary);
        return ary;
    }
    
    int main() {
        std::tuple<A, B, A, A, B, A> foo;
        auto ary = tuple_to_base_ptr_array(foo);
        for (auto* ptr : ary) {
            std::cout << ptr->foo();
        }
        std::cout << "\n";
        return 0;
    }