How can I create a n way Cartesian product of type lists in C++?

后端 未结 3 621
既然无缘
既然无缘 2020-12-30 23:45

Self explanatory.

Basically, say I have type lists like so:

using type_list_1 = type_list;
using type_list_2 = type_list

        
相关标签:
3条回答
  • Ok, got it. It's not pretty but it works:

    template<class ... T>
    struct type_list{};
    
    struct somestructA{};
    struct somestructB{};
    
    using type_list_1 = type_list<int, somestructA, char>;
    using type_list_2 = type_list<somestructB>;
    using type_list_3 = type_list<double, short, float>;
    
    template<class TL1, class TL2>
    struct add;
    
    template<class ... T1s, class ... T2s>
    struct add<type_list<T1s...>, type_list<T2s...>>
    {
        using type = type_list<T1s..., T2s...>;
    };
    
    template<class ... TL>
    struct concat;
    
    template<class TL, class ... TLs>
    struct concat<TL, TLs...>
    {
        using type = typename add<TL, typename concat<TLs...>::type>::type;
    };
    
    template<class TL>
    struct concat<TL>
    {
        using type = TL;
    };
    
    static_assert(std::is_same_v<type_list<int, somestructA, char, double, short, float>, typename add<type_list_1, type_list_3>::type>);
    
    template<class TL1, class TL2>
    struct multiply_one;
    
    // Prepends each element of T1 to the list T2.
    template<class ... T1s, class ... T2s>
    struct multiply_one<type_list<T1s...>, type_list<T2s...>>
    {
        using type = typename concat<type_list<type_list<T1s, T2s...>...>>::type;
    };
    
    static_assert(std::is_same_v<
        type_list<
            type_list<int, double, short, float>,
            type_list<somestructA, double, short, float>,
            type_list<char, double, short, float>
            >,
        typename multiply_one<type_list_1, type_list_3>::type>);
    
    // Prepends each element of TL to all type lists in TLL.
    template<class TL, class TLL>
    struct multiply_all;
    
    template<class TL, class ... TLs>
    struct multiply_all<TL, type_list<TLs...>>
    {
        using type = typename concat<typename multiply_one<TL, TLs>::type...>::type;
    };
    
    static_assert(std::is_same_v<
        type_list<
            type_list<int, double, short, float>,
            type_list<somestructA, double, short, float>,
            type_list<char, double, short, float>
            >,
        typename multiply_all<type_list_1, type_list<type_list_3>>::type>);
    
    static_assert(std::is_same_v<
        type_list<
            type_list<int, somestructB>,
            type_list<somestructA, somestructB>,
            type_list<char, somestructB>,
            type_list<int, double, short, float>,
            type_list<somestructA, double, short, float>,
            type_list<char, double, short, float>
            >,
        typename multiply_all<type_list_1, type_list<type_list_2, type_list_3>>::type>);
    
    template<class TL, class ... TLs>
    struct cartesian_product
    {
        using type = typename multiply_all<TL, typename cartesian_product<TLs...>::type>::type;
    };
    
    template<class ... Ts>
    struct cartesian_product<type_list<Ts...>>
    {
        using type = type_list<type_list<Ts>...>;
    };
    
    
    using expected_result = type_list<
        type_list<int, somestructB, double>,
        type_list<somestructA, somestructB, double>,
        type_list<char, somestructB, double>,
        type_list<int, somestructB, short>,
        type_list<somestructA, somestructB, short>,
        type_list<char, somestructB, short>,
        type_list<int, somestructB, float>,
        type_list<somestructA, somestructB, float>,
        type_list<char, somestructB, float>
    >;
    
    static_assert(std::is_same_v<expected_result,
        cartesian_product<type_list_1, type_list_2, type_list_3>::type>);
    

    https://godbolt.org/z/L5eamT

    I left my own static_assert tests in there for... Well, I hope they help.

    Also, I'm sure there has to be a nicer solution. But this was the obvious "I know this will eventually lead to the goal" path. I eventually had to resort to adding a concat or sorts, I'm sure that it could be used much earlier to skip most of the cruft.

    0 讨论(0)
  • 2020-12-31 00:11

    With Boost.Mp11, this is a short one-liner (as always):

    using result = mp_product<
        type_list,
        type_list_1, type_list_2, type_list_3>;
    

    Demo.

    0 讨论(0)
  • 2020-12-31 00:12

    Fold expressions to the rescue again

    template<typename... Ts>
    typelist<typelist<Ts>...> layered(typelist<Ts...>);
    
    template<typename... Ts, typename... Us>
    auto operator+(typelist<Ts...>, typelist<Us...>)
        -> typelist<Ts..., Us...>;
    
    template<typename T, typename... Us>
    auto operator*(typelist<T>, typelist<Us...>)
        -> typelist<decltype(T{} + Us{})...>;
    
    template<typename... Ts, typename TL>
    auto operator^(typelist<Ts...>, TL tl)
        -> decltype(((typelist<Ts>{} * tl) + ...));
    
    template<typename... TLs>
    using product_t = decltype((layered(TLs{}) ^ ...));
    

    And you're done. This has the additional benefit over recursion of having O(1) instantiation depth.

    struct A0;
    struct A1;
    struct B0;
    struct B1;
    struct C0;
    struct C1;
    struct C2;
    
    using t1 = typelist<A0, A1>;
    using t2 = typelist<B0, B1>;
    using t3 = typelist<C0, C1, C2>; 
    
    using p1 = product_t<t1, t2>;
    using p2 = product_t<t1, t2, t3>;
    
    using expect1 = typelist<typelist<A0, B0>,
                             typelist<A0, B1>,
                             typelist<A1, B0>,
                             typelist<A1, B1>>;
    
    using expect2 = typelist<typelist<A0, B0, C0>,
                             typelist<A0, B0, C1>,
                             typelist<A0, B0, C2>,
                             typelist<A0, B1, C0>,
                             typelist<A0, B1, C1>,
                             typelist<A0, B1, C2>,
                             typelist<A1, B0, C0>,
                             typelist<A1, B0, C1>,
                             typelist<A1, B0, C2>,
                             typelist<A1, B1, C0>,
                             typelist<A1, B1, C1>,
                             typelist<A1, B1, C2>>;
    
    static_assert(std::is_same_v<p1, expect1>);
    static_assert(std::is_same_v<p2, expect2>);
    
    0 讨论(0)
提交回复
热议问题