How to create the Cartesian product of a type list?

前端 未结 9 1392
夕颜
夕颜 2020-11-28 09:15

I\'d like to create the cross product of a list of types using variadic templates.

Here\'s what I have so far:

#include 
#include <         


        
相关标签:
9条回答
  • 2020-11-28 10:11

    Maybe something like this:

    template <typename ...Args> struct typelist { };
    
    template <typename S, typename T> struct typelist_cat;
    
    template <typename ...Ss, typename ...Ts>
    struct typelist_cat<typelist<Ss...>, typelist<Ts...>>
    {
        typedef typelist<Ss..., Ts...> type;
    };
    
    
    template <typename S, typename T> struct product;
    
    template <typename S, typename ...Ss, typename ...Ts>
    struct product<typelist<S, Ss...>, typelist<Ts...>>
    {
        // the cartesian product of {S} and {Ts...}
        // is a list of pairs -- here: a typelist of 2-element typelists
        typedef typelist<typelist<S, Ts>...> S_cross_Ts;
    
        // the cartesian product of {Ss...} and {Ts...} (computed recursively)
        typedef typename product<typelist<Ss...>, typelist<Ts...>>::type
            Ss_cross_Ts;
    
        // concatenate both products
        typedef typename typelist_cat<S_cross_Ts, Ss_cross_Ts>::type type;
    };
    
    // end the recursion
    template <typename ...Ts>
    struct product<typelist<>, typelist<Ts...>>
    {
        typedef typelist<> type;
    };
    

    Now you should be able to use product<typelist<A,B,C>, typelist<D,E,F>>::type.

    0 讨论(0)
  • 2020-11-28 10:11

    Note: This is NOT what the OP asked for... but may be of relevance to others (like me) who stumble upon this question. Here is how it can be done using a Loki::TypeList (i.e. prior C++-11), perhaps of historical interest or for compatability sake.

    Also, perhaps it is presumptuous of me to pollute loki's namespace. YMMV.

    crossproduct.h

    #include "loki/NullType.h"
    #include "loki/Typelist.h"
    
    namespace Loki {
    namespace   TL {
    
    /// a pair of two types
    template <typename A_t, typename B_t>
    struct TypePair
    {
        typedef A_t A;
        typedef B_t B;
    };
    
    
    /// a template which takes one type and pairs it with all other types
    /// in another typelist
    template <class T, class TList > struct DistributePair;
    
    /// specialization of Distribute for the nulltype
    template < class TList >
    struct DistributePair< NullType, TList >
    {
         typedef NullType type;
    };
    
    
    /// specialization of Distribute where the second parameter is nulltype
    template <class T >
    struct DistributePair< T, NullType >
    {
         typedef NullType type;
    };
    
    /// specialization of Distribute where the first parameter is a
    /// typelist
    template <class T, class Head, class Tail >
    struct DistributePair< T, Typelist<Head,Tail> >
    {
         typedef Typelist<
                     TypePair<T,Head>,
                     typename DistributePair<T,Tail>::type
                         > type;
    };
    
    /// performs cartesion product of two typelists
    template <class TListA, class TListB> struct CrossProduct;
    
    /// specialization of CrossProduct for NullType
    template <class TListB>
    struct CrossProduct< NullType, TListB >
    {
        typedef NullType type;
    };
    
    /// specialization of CrossProduct for recursion
    template <class Head, class Tail, class TListB>
    struct CrossProduct< Typelist<Head,Tail>, TListB >
    {
        typedef typename Append<
                typename DistributePair< Head,TListB >::type,
                typename CrossProduct< Tail, TListB >::type
                  >::Result type;
    };
    
    } // namespace TL
    } // namespace Loki
    

    test.cpp

    #include <crossproduct.h>
    #include <loki/HierarchyGenerators.h>
    #include <iostream>
    
    struct A{};
    struct B{};
    struct C{};
    
    struct D{};
    struct E{};
    struct F{};
    
    typedef LOKI_TYPELIST_3(A,B,C)  TypeListA_t;
    typedef LOKI_TYPELIST_3(D,E,F)  TypeListB_t;
    
    typedef typename Loki::TL::CrossProduct< TypeListA_t, TypeListB_t >::type Cross_t;
    
    template <typename T> const char* toString();
    
    template <> const char* toString<A>(){ return "A"; };
    template <> const char* toString<B>(){ return "B"; };
    template <> const char* toString<C>(){ return "C"; };
    template <> const char* toString<D>(){ return "D"; };
    template <> const char* toString<E>(){ return "E"; };
    template <> const char* toString<F>(){ return "F"; };
    
    template <typename T> struct Printer
    {
        Printer()
        {
            std::cout << toString<T>() << ", ";
        }
    };
    
    template <typename T1, typename T2>
    struct Printer< Loki::TL::TypePair<T1,T2> >
    {
        Printer()
        {
            std::cout << "(" << toString<T1>() << "," << toString<T2>() << "), ";
        }
    };
    
    
    typedef Loki::GenScatterHierarchy< TypeListA_t, Printer > PrinterA_t;
    typedef Loki::GenScatterHierarchy< TypeListB_t, Printer > PrinterB_t;
    typedef Loki::GenScatterHierarchy< Cross_t, Printer >     PrinterCross_t;
    
    int main(int argc, char** argv)
    {
        std::cout << "\nType list A: \n   ";
        PrinterA_t a;
        std::cout << "\nType list B: \n   ";
        PrinterB_t b;
        std::cout << "\nType list Cross: \n   ";
        PrinterCross_t cross;
    
        return 0;
    }
    

    output

    Type list A: 
       A, B, C, 
    Type list B: 
       D, E, F, 
    Type list Cross: 
       (A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F), 
    
    0 讨论(0)
  • 2020-11-28 10:16

    A nice clean version I think:

    cross_product.cpp:

    #include "type_printer.hpp"
    
    #include <iostream>
    
    template<typename ...Ts> struct type_list {};
    template<typename T1, typename T2> struct pair {};
    
    // Concatenation
    template <typename ... T> struct concat;
    template <typename ... Ts, typename ... Us>
    struct concat<type_list<Ts...>, type_list<Us...>>
    {
        typedef type_list<Ts..., Us...> type;
    };
    
    // Cross Product
    template <typename T, typename U> struct cross_product;
    
    // Partially specialise the empty case for the first type_list.
    template <typename ...Us>
    struct cross_product<type_list<>, type_list<Us...>> {
        typedef type_list<> type;
    };
    
    // The general case for two type_lists. Process:
    // 1. Expand out the head of the first type_list with the full second type_list.
    // 2. Recurse the tail of the first type_list.
    // 3. Concatenate the two type_lists.
    template <typename T, typename ...Ts, typename ...Us>
    struct cross_product<type_list<T, Ts...>, type_list<Us...>> {
        typedef typename concat<
            type_list<pair<T, Us>...>,
            typename cross_product<type_list<Ts...>, type_list<Us...>>::type
        >::type type;
    };
    
    struct A {};
    struct B {};
    struct C {};
    struct D {};
    struct E {};
    struct F {};
    
    template <typename T, typename U>
    void test()
    {
        std::cout << print_type<T>() << " \u2a2f " << print_type<U>() << " = "
            << print_type<typename cross_product<T, U>::type>() << std::endl;
    }
    
    int main()
    {
        std::cout << "Cartesian product of type lists\n";
        test<type_list<>, type_list<>>();
        test<type_list<>, type_list<A>>();
        test<type_list<>, type_list<A, B>>();
        test<type_list<A, B>, type_list<>>();
        test<type_list<A>, type_list<B>>();
        test<type_list<A>, type_list<B, C, D>>();
        test<type_list<A, B>, type_list<B, C, D>>();
        test<type_list<A, B, C>, type_list<D>>();
        test<type_list<A, B, C>, type_list<D, E, F>>();
        return 0;
    }
    

    type_printer.hpp:

    #ifndef TYPE_PRINTER_HPP
    #define TYPE_PRINTER_HPP
    
    #include "detail/type_printer_detail.hpp"
    
    template <typename T>
    std::string print_type()
    {
        return detail::type_printer<T>()();
    }
    
    #endif
    

    detail/type_printer_detail.hpp:

    #ifndef DETAIL__TYPE_PRINTER_DETAIL_HPP
    #define DETAIL__TYPE_PRINTER_DETAIL_HPP
    
    #include <typeinfo>
    #include <cxxabi.h>
    #include <string>
    
    template <typename ...Ts> struct type_list;
    template <typename T1, typename T2> struct pair;
    
    namespace detail {
    
    // print scalar types
    template <typename T>
    struct type_printer {
        std::string operator()() const {
            int s;
            return abi::__cxa_demangle(typeid(T).name(), 0, 0, &s);
        }   
    };
    
    // print pair<T, U> types
    template <typename T, typename U>
    struct type_printer<pair<T, U>> {
        std::string operator()() const {
            return "(" + type_printer<T>()() + "," + type_printer<U>()() + ")";
        }   
    };
    
    // print type_list<T>
    template <>
    struct type_printer<type_list<>> {
        std::string operator()() const {
            return "\u2205";
        }   
    };
    
    template <typename T>
    struct type_printer<type_list<T>> {
        std::string operator()() const {
            return "{" + type_printer<T>()() + "}";
        }   
        std::string operator()(const std::string& sep) const {
            return sep + type_printer<T>()();
        }   
    };
    
    template <typename T, typename ...Ts>
    struct type_printer<type_list<T, Ts...>> {
        std::string operator()() const {
            return "{" + type_printer<T>()() + type_printer<type_list<Ts...>>()(std::string(", ")) + "}";
        }   
        std::string operator()(const std::string& sep) const {
            return sep + type_printer<T>()() + type_printer<type_list<Ts...>>()(sep);
        }   
    };
    }
    
    #endif
    

    Run:

    g++ -std=c++0x cross_product.cpp && ./a.out
    

    Output:

    Cartesian product of type lists
    ∅ ⨯ ∅ = ∅
    ∅ ⨯ {A} = ∅
    ∅ ⨯ {A, B} = ∅
    {A, B} ⨯ ∅ = ∅
    {A} ⨯ {B} = {(A,B)}
    {A} ⨯ {B, C, D} = {(A,B), (A,C), (A,D)}
    {A, B} ⨯ {B, C, D} = {(A,B), (A,C), (A,D), (B,B), (B,C), (B,D)}
    {A, B, C} ⨯ {D} = {(A,D), (B,D), (C,D)}
    {A, B, C} ⨯ {D, E, F} = {(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F)}
    

    (I noticed on Windows using Chrome that the cross product unicode character is not coming out well. Sorry, I don't know how to fix that.)

    0 讨论(0)
提交回复
热议问题