Search code examples
c++templatesc++17variadic-templatesparameter-pack

unpack a parameter pack of pairs into an array and a tuple


So i have a list of pairs where the first member is a constant integer and the second is a type, is there anyway to unpack it into an array of the first member and a tuple of the second members?

struct MA {}
struct MB {}
struct MC {}
template <int I, class T> struct MyPair{};

How can I make a template meta function such that it has these two members:

MyStruct<1, MA, 2, MB, 3, MC> {
     std::array<int, 3> arr = {1, 2, 3};
     using Tuple = std::tuple<MA, MB, MC>;
};

Solution

  • Just define two helper metafunctions to get I and T:

    template<class> struct GetFirst;
    template<int I, class T> struct GetFirst<MyPair<I, T>> {
        static constexpr int value = I;
    };
    
    template<class> struct GetSecond;
    template<int I, class T> struct GetSecond<MyPair<I, T>> {
        using type = T;
    };
    
    template<class... MyPairs>
    struct MyStruct {
        std::array<int, sizeof...(MyPairs)> arr{GetFirst<MyPairs>::value...};
        using Tuple = std::tuple<typename GetSecond<MyPairs>::type...>;
    };
    
    //////////////////////////////////////////////////
    
    using S = MyStruct<MyPair<1, MA>, MyPair<2, MB>, MyPair<3, MC>>;
    static_assert(std::is_same_v<S::Tuple, std::tuple<MA, MB, MC>>);
    assert((S{}.arr == std::array{1, 2, 3}));
    

    You can't mix type and non-type parameters in a variadic template, so it's not possible to have

    MyStruct<1, MA, 2, MB, 3, MC, ...>
    

    without wrapping (int, Type) into a type.


    As JeJo mentioned in a comment below, both metafunctions can be joined into a single one:

    template<class> struct MyPairTraits;
    template<int I, class T> struct MyPairTraits<MyPair<I, T>> {
        static constexpr int i = I;
        using Type = T;
    };