Search code examples
c++metaprogrammingc++20boost-mp11

Transformation of compile time list of lists of types into a list of lists of indices


I would like to transform a compile time list of lists of types into a list of lists of std::integral_constant indices. Appearently this requires some kind of 2d iota-like transformation.

Example source

template<typename... Ts> struct type_list{};
using L = type_list<
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<int>,
    type_list<>,
    type_list<int>,
    type_list<int>,
    type_list<int>,
    type_list<int,double>,
    type_list<int,double>
>;

expected result

template<uint64_t V> using I = std::integral_constant<uint64_t, V>; // alias for better legibility
using R = type_list<
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<I<0>>,
    type_list<>,
    type_list<I<1>>,
    type_list<I<2>>,
    type_list<I<3>>,
    type_list<I<4>, I<5>>,
    type_list<I<6>, I<7>>
>;

I think boost::mp11 fits this purpose perfectly, but I lack the experience to bring it all together. Please share your advice.


Solution

  • I think boost::mp11 fits this purpose perfectly, but I lack the experience to bring it all together. Please share your advice.

    You are correct! Boost.Mp11 does fit this purpose perfectly.

    This is basically a stateful fold. We have to keep track of how many values we've pushed so far, call that N. Then, for each new element of the list, L, that we're iterating on, we append a new list that is [N, N+1, ..., N+len(L)-1] and then do N += len(L) in the state.

    For convenience, we can add a State:

    template <class N, class L>
    struct State {
        using value = N;
        using list = L;
    };
    

    An accumulating function that does what I described above:

    template <class S, class L>
    using F = State<
        // S::value + len(L)
        mp_plus<typename S::value, mp_size<L>>,
        mp_push_back<typename S::list,
            // iota just gives you [0, 1, ..., V-1]
            // so we need to add S::value to all of those
            mp_transform_q<
                mp_bind_front<mp_plus, typename S::value>,
                mp_iota<mp_size<L>>>>
        >;
    

    And then lastly, we do the fold and pull out the list:

    template <class L>
    using types_to_ints = mp_fold<L, State<mp_size_t<0>, mp_list<>>, F>::list
    

    Example demonstrating that it does the right thing.