5

Powiedzmy mamTworzenie kartezjański ekspansję iloczyn dwóch zmiennej liczbie argumentów, dla typu parametru szablonu pakuje

  • dwa wykazy non typu parameteres szablonów (które mogą mieć inny typ)
  • szablon foo że ma jedną wartość każdej z tych list jako parametr

jak mogę stworzyć pakiet o zmiennej liczbie argumentów parametru foo s, parametryczne z iloczyn kartezjański dwóch lista elementów?

Oto co mam na myśli:

template<int ...> 
struct u_list {}; 

template<char ...> 
struct c_list {}; 

template<int, char > 
struct foo {}; 

template<class ...> 
struct bar {}; 

using int_vals = u_list<1, 5, 7>; 
using char_vals = c_list<-3, 3>; 


using result_t = /* magic happens*/ 
using ref_t = bar< 
    foo<1, -3>, foo<1, 3>, 
    foo<5, -3>, foo<5, 3>, 
    foo<7, -3>, foo<7, 3> 
>; 

static_assert(std::is_same<result_t, ref_t >::value, ""); 

szukam rozwiązanie, które działa w C++ 11 i nie korzysta z żadnych bibliotek wyjątkiem C++ 11 biblioteki standardowej. Mam również moją ręcznie napisaną wersję C++ 14-ego index_sequence/make_index_sequence i mogę dostarczyć listy parametrów bez typów jako tablice, jeśli to upraszcza kod.

Najbliższe, jakie udało mi się znaleźć, to: How to create the Cartesian product of a type list?. Zasadniczo (nie przetestowałem tego) powinno być możliwe przekształcenie pakietów parametrów bez typów w pakiety parametrów, a następnie zastosowanie rozwiązania w połączonym poście, ale miałem nadzieję, że istnieje prostsze/krótsze rozwiązanie wzdłuż linie tego:

template<int... Ints, char ... Chars> 
auto magic(u_list<Ints...>, c_list<Chars...>) 
{ 
    //Doesn't work, as it tries to expand the parameter packs in lock step 
    return bar<foo<Ints,Chars>...>{}; 
} 

using result_t = decltype(magic(int_vals{}, char_vals{})); 
+0

Zrobiłem kilka nieprzyjemnych rzeczy z kombinacjami parametrów ... może to może być pomocne: [\ [kliknij mnie \]] (https://stackoverflow.com/questions/39687907/is-it-possible -to-invoke-a-method-with-all-possible-k-combination-with-repetit) –

Odpowiedz

2

Można zrobić coś jak następuje:

template <int... Is> 
using u_list = std::integer_sequence<int, Is...>; 

template <char... Cs> 
using c_list = std::integer_sequence<char, Cs...>; 

template<int, char> struct foo {}; 

template<class ...> struct bar {}; 

template <std::size_t I, typename T, template <typename, T...> class C, T ... Is> 
constexpr T get(C<T, Is...> c) 
{ 
    constexpr T values[] = {Is...}; 
    return values[I]; 
} 


template <std::size_t I, typename T> 
constexpr auto get_v = get<I>(T{}); 


template<int... Ints, char ... Chars, std::size_t ... Is> 
auto cartesian_product(u_list<Ints...>, c_list<Chars...>, std::index_sequence<Is...>) 
-> bar<foo< 
     get_v<Is/sizeof...(Chars), u_list<Ints...> >, 
     get_v<Is % sizeof...(Chars), c_list<Chars...> > 
     >... 
    >; 

template<int... Ints, char ... Chars> 
auto cartesian_product(u_list<Ints...> u, c_list<Chars...> c) 
-> decltype(cartesian_product(u, c, std::make_index_sequence<sizeof...(Ints) * sizeof...(Chars)>())); 




using int_vals = u_list<1, 5, 7>; 
using char_vals = c_list<-3, 3>; 

using result_t = decltype(cartesian_product(int_vals{}, char_vals{})); 

Demo

Możliwa realizacja std części:

template <typename T, T ... Is> struct integer_sequence{}; 

template <std::size_t ... Is> 
using index_sequence = integer_sequence<std::size_t, Is...>; 

template <std::size_t N, std::size_t... Is> 
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {}; 

template <std::size_t... Is> 
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {}; 

I zmienić w odpowiedzi:

template <std::size_t I, typename T, template <typename, T...> class C, T ... Is> 
constexpr T get(C<T, Is...> c) 
{ 
    using array = T[]; 
    return array{Is...}[I]; 
} 

template<int... Ints, char ... Chars, std::size_t ... Is> 
auto cartesian_product(u_list<Ints...>, c_list<Chars...>, index_sequence<Is...>) 
-> bar<foo< 
     get<Is/sizeof...(Chars)>(u_list<Ints...>{}), 
     get<Is % sizeof...(Chars)>(c_list<Chars...>{}) 
     >... 
    >; 

Demo C++11

+0

Dziękuję. Na pierwszy rzut oka wydaje się to rozwiązaniem, które jest dla mnie najbardziej czytelne. Będę musiał trochę poeksperymentować z prawdziwym kodem, aby zobaczyć, która odpowiedź będzie dla mnie najlepsza. – MikeMB

+0

Czy możesz przetłumaczyć to na rozwiązanie C++ 11? – MikeMB

+0

@MikeMB: Dodano wersję C++ 11. – Jarod42

2

Wykonanie szablonu metaprogramowania w domenie czystych typów jest w mojej opinii znacznie łatwiejsze.

Trochę pracy wymaga przeniesienia z kraju parametrów szablonu non-type do rodzaju gruntów iz powrotem, ale oznacza to, że używasz ogólnych narzędzi do metaprogramowania zamiast tych specyficznych dla twojego problemu.


Więc zmniejszę twój problem do produktu kartezjańskiego na liście typów.

Oto mój typ pakietu:

template<class...Ts>struct types { 
    using type=types; // makes inheriting from it useful 
    static constexpr std::size_t size = sizeof...(Ts); 
}; 

Najpierw piszemy fmap. Fmap przyjmuje funkcję i listę oraz zwraca listę każdego elementu listy z zastosowaną funkcją.

template<template<class...>class Z, class List> 
struct fmap {}; 
template<template<class...>class Z, class List> 
using fmap_t = typename fmap<Z,List>::type; 
template<template<class...>class Z, class...Ts> 
struct fmap<Z, types<Ts...>>: 
    types<Z<Ts>...> 
{}; 

i fapply. fapply przyjmuje również funkcję i listę, ale stosuje tę funkcję do całego zestawu elementów listy.

template<template<class...>class Z, class List> 
struct fapply {}; 
template<template<class...>class Z, class List> 
using fapply_t=typename fapply<Z,List>::type; 
template<template<class...>class Z, class...Ts> 
struct fapply<Z, types<Ts...>> { 
    using type=Z<Ts...>; 
}; 

Jak to się dzieje, częściowe zastosowanie fapply jest bardzo przydatne:

template<template<class...>class Z> 
struct applier { 
    template<class List> 
    using apply = fapply_t<Z,List>; 
}; 

będziemy chcieli, aby móc concatinate list:

template<class...> 
struct cat:types<> {}; 
template<class...As, class...Bs, class...Cs> 
struct cat<types<As...>, types<Bs...>, Cs...>: 
    cat<types<As..., Bs...>, Cs...> 
{}; 
template<class...As> 
struct cat<types<As...>>:types<As...>{}; 
template<class...Ts>using cat_t=typename cat<Ts...>::type; 

Następnie tutaj jest cart_product_t:

template<class A, class B> 
struct cart_product {}; 
template<class A, class B> 
using cart_product_t = typename cart_product<A,B>::type; 
template<class A, class... Bs> 
struct cart_product<types<A>, types<Bs...>>: 
    types< types<A, Bs>... > 
{}; 
// reduce cart_product to cart_product on a one element list on the lhs: 
template<class...As, class... Bs> 
struct cart_product<types<As...>, types<Bs...>>: 
    fapply_t< 
    cat_t, 
    fmap_t< 
     applier<cart_product_t>::template apply, 
     types< 
     types< types<As>, types<Bs...> >... 
     > 
    > 
    > 
{}; 

Rodzaje specyficzne dla swojego problemu:

template<int...>struct u_list {}; 
template<char...>struct c_list {}; 
template<int, char>struct foo {}; 
template<class...>struct bar{}; 

narzędziem wyciągu listy wartości do typów:

template<class> struct lift {}; 
template<int...is> struct lift<u_list<is...>>: 
    types< std::integral_constant<int, is>... > 
{}; 
template<char...is> struct lift<c_list<is...>>: 
    types< std::integral_constant<char, is>... > 
{}; 
template<class T>using lift_t=typename lift<T>::type; 

lower_to_foo trwa parę typów, i konwertuje je do foo:

template<class I, class C> 
using lower_to_foo = foo<I::value, C::value>; 

połączyliśmy je teraz:

using int_vals = u_list<1, 5, 7>; 
using char_vals = c_list<-3, 3>; 

using product = cart_product_t< lift_t<int_vals>, lift_t<char_vals> >; 
static_assert(product::size == 6, "should be 6"); 
using result_t = fapply_t< bar, fmap_t< applier<lower_to_foo>::template apply, product > >; 

using ref_t = bar< 
    foo<1, -3>, foo<1, 3>, 
    foo<5, -3>, foo<5, 3>, 
    foo<7, -3>, foo<7, 3> 
>; 
ref_t test = result_t{}; // gives better error messages than static_assert 
static_assert(std::is_same<result_t, ref_t >::value, ""); 

a bob to twój wujek.

cat, fmap i fapply są względnie standardowymi funkcjami w programowaniu funkcjonalnym. applier po prostu pozwala pisać funkcje mapowania szablonów, pobierając elementy zamiast list (jest to częściowo stosowane fapply).

Live example.


Teraz, pamiętasz, jak powiedziałem, że metaprogramowanie szablonu było łatwiejsze z typami?

Należy zwrócić uwagę na wszystkie te parametry szablonu szablonu? Łatwiej jest, jeśli są typami.

template<template<class...>class Z> 
struct ztemplate { 
    template<class...Ts>using apply=Z<Ts...>; 
}; 

i można przejść całą drogę w dół do Hana stylu constexpr metaprogramowanie ze znacznikami typu i operator() na ztemplate i inne zabawy.

+0

Czy std :: integral_constant rzeczywiście musi mieć typ integralny lub jest np. możliwe są również wyliczenia klas? – MikeMB

+0

@MikeMB Mate, wskaźniki funkcji są nawet możliwe. Można je wywoływać nawet w C++ 14. – Yakk

+0

Dziękuję. Wydaje się, że to dobre rozwiązanie, ale chyba że potrzebujemy więcej tmp w naszym kodzie, jestem trochę niechętny do dodania tylu ogólnych udogodnień dla mojego pojedynczego zastosowania. – MikeMB

0

Biorąc typu listy przekrój produkt jako baza

#include <iostream> 
#include <typeinfo> 
#include <cxxabi.h> 

template<int ...> struct u_list {}; 

template<char ...> struct c_list {}; 

template<int, char > struct foo {}; 

template<typename...> struct type_list {}; 

Rozszerzamy pakiet char... z row

template<int I, char... Cs> 
    struct row 
{ 
    typedef type_list<foo<I,Cs>...> type; 
}; 

template <typename... T> struct concat; 

template <typename... S, typename... T> 
struct concat<type_list<S...>, type_list<T...>> 
{ 
    using type = type_list<S..., T...>; 
}; 

Chcemy dodatkową specjalizację concat wyrwać się z przypadkiem bazowym

template <typename... T> 
struct concat<type_list<T...>, void> 
{ 
    using type = type_list<T...>; 
}; 

template<typename I, typename C> 
struct cross_product; 

Podstawowy futerał: bez Re wskazówki

template<char... Cs> 
struct cross_product<u_list<>, c_list<Cs...>> 
{ 
    using type = void; 
}; 

Recursive przypadek: int, a następnie przez stado wskazówki

template<int I, int... Is, char... Cs> 
struct cross_product<u_list<I, Is...>, c_list<Cs...>> 
{ 
    using type = typename concat<typename row<I,Cs...>::type, typename cross_product<u_list<Is...>, c_list<Cs...>>::type>::type; 

}; 

int main() 
{ 
    using int_vals = u_list<1, 5, 7>; 
    using char_vals = c_list<-3, 3>; 

    using result_t = cross_product<int_vals, char_vals>::type; 
    using ref_t = type_list< 
     foo<1, -3>, foo<1, 3>, 
     foo<5, -3>, foo<5, 3>, 
     foo<7, -3>, foo<7, 3> 
    >; 

    static_assert(std::is_same<result_t, ref_t >::value, ""); 
    return 0; 
} 

Live on Coliru!

0

Następujące są moje 2 centy ...

Jeśli chcesz rodzajowy rozwiązanie, większy problem widzę, że od int_vals i char_vals typy nie jest łatwe (w C++ 11; prostsze w C++ 17) wyodrębnij typy zawartych wartości (int i char).

Więc załóżmy, że masz do przekazania ich do magic<> wraz z foo i bar (jeśli nie chcesz zakodowane foo i bar).

Więc wywołanie magic<> stać się (w drodze)

using result_t 
    = typename magic<int, char, foo, bar, int_vals, char_vals>::type; 

Poniżej znajduje się pełna przykładów pracy z mojego rozwiązania.

#include <type_traits> 

template <int...> struct u_list {}; 
template <char...> struct c_list {}; 

template <int, char> struct foo {}; 
template <typename ...> struct bar {}; 

template <typename T1, typename T2, T1 t1, T2 ... T2s> 
struct midProd 
{ }; 

template <typename T1, typename T2, template <T1, T2> class, typename...> 
struct magicHelper; 

template <typename T1, typename T2, 
      template <T1, T2> class ResIn, 
      template <typename...> class ResOut, 
      typename ... R> 
struct magicHelper<T1, T2, ResIn, ResOut<R...>> 
{ using type = ResOut<R...>; }; 

template <typename T1, typename T2, 
      template <T1, T2> class ResIn, 
      template <typename...> class ResOut, 
      typename ... R, T1 ts1, T2 ... ts2, typename ... MpS> 
struct magicHelper<T1, T2, ResIn, ResOut<R...>, 
       midProd<T1, T2, ts1, ts2...>, MpS...> 
{ using type = typename magicHelper<T1, T2, ResIn, 
        ResOut<R..., ResIn<ts1, ts2>...>, MpS...>::type; }; 


template <typename T1, typename T2, 
      template <T1, T2> class, 
      template <typename...> class, 
      typename, typename> 
struct magic; 

template <typename T1, typename T2, 
      template <T1, T2> class ResIn, 
      template <typename...> class ResOut, 
      template <T1...> class C1, template <T2...> class C2, 
      T1 ... ts1, T2 ... ts2> 
struct magic<T1, T2, ResIn, ResOut, C1<ts1...>, C2<ts2...>> 
{ using type = typename magicHelper<T1, T2, ResIn, ResOut<>, 
        midProd<T1, T2, ts1, ts2...>...>::type ; }; 

int main() 
{ 
    using int_vals = u_list<1, 5, 7>; 
    using char_vals = c_list<-3, 3>; 

    using result_t 
     = typename magic<int, char, foo, bar, int_vals, char_vals>::type; 

    using ref_t = bar< foo<1, -3>, foo<1, 3>, 
         foo<5, -3>, foo<5, 3>, 
         foo<7, -3>, foo<7, 3> >; 

    static_assert(std::is_same<result_t, ref_t >::value, ""); 
} 

Oczywiście, jeśli wolisz twarde kod niektóre typy (u_list, c_list, foo i bar), roztwór staje się dużo prostsze

#include <type_traits> 

template <int...> struct u_list {}; 
template <char...> struct c_list {}; 

template <int, char> struct foo {}; 
template <typename ...> struct bar {}; 

template <int, char...> struct midProd {}; 

template <typename...> 
struct magicH; 

template <typename ... R> 
struct magicH<bar<R...>> 
{ using type = bar<R...>; }; 

template <typename ... R, int i, char ... cs, typename ... MpS> 
struct magicH<bar<R...>, midProd<i, cs...>, MpS...> 
{ using type = typename magicH<bar<R..., foo<i, cs>...>, MpS...>::type; }; 


template <typename, typename> 
struct magic; 

template <int ... is, char ... cs> 
struct magic<u_list<is...>, c_list<cs...>> 
{ using type = typename magicH<bar<>, midProd<is, cs...>...>::type; }; 

int main() 
{ 
    using int_vals = u_list<1, 5, 7>; 
    using char_vals = c_list<-3, 3>; 

    using result_t = typename magic<int_vals, char_vals>::type; 

    using ref_t = bar< foo<1, -3>, foo<1, 3>, 
         foo<5, -3>, foo<5, 3>, 
         foo<7, -3>, foo<7, 3> >; 

    static_assert(std::is_same<result_t, ref_t >::value, ""); 
} 
0

Tak samo jak inni w C++ 17 :

// Type your code here, or load an example. 
#include <type_traits> 

template<int ...> 
struct u_list {}; 

template<char ...> 
struct c_list {}; 

template<int, char > 
struct foo {}; 

template<class ...> 
struct bar {}; 

using int_vals = u_list<1, 5, 7>; 
using char_vals = c_list<-3, 3>; 

template<class... Args> struct type_list{ 
    template<class> struct make_concat; 
    template<class ...Xs> 
    struct make_concat<type_list<Xs...>>{ 
     using type = type_list<Args...,Xs...>; 
    }; 
    template<class T> 
    using concat = typename make_concat<T>::type; 
    template<template<class...>class TT> 
    using applied_to = TT<Args...>; 
}; 

template< 
     template<auto,auto> class C 
     ,class X,class Y,class Yit=Y> 
struct cart_prod; 
template<template<auto,auto> class C, 
     template<auto...> class Xt, 
     template<auto...> class Yt, 
     class Yit, 
     auto Xi,auto...Xis,auto Yi,auto...Yis> 
struct cart_prod<C,Xt<Xi,Xis...>,Yt<Yi,Yis...>,Yit>{ 
    using type = typename type_list<class C<Xi,Yi>> 
     ::template concat<typename cart_prod<C,Xt<Xi,Xis...>,Yt<Yis...>,Yit>::type>; 
}; 
template<template<auto,auto> class C, 
     template<auto...> class Xt, 
     template<auto...> class Yt, 
     class Yit, 
     auto Xi,auto...Xis,auto Yi> 
struct cart_prod<C,Xt<Xi,Xis...>,Yt<Yi>,Yit>{ 
    using type = typename type_list<class C<Xi,Yi>> 
     ::template concat<typename cart_prod<C,Xt<Xis...>,Yit,Yit>::type>; 
}; 
template<template<auto,auto> class C, 
     template<auto...> class Xt, 
     template<auto...> class Yt, 
     class Yit, 
     auto Xi,auto Yi> 
struct cart_prod<C,Xt<Xi>,Yt<Yi>,Yit>{ 
    using type = type_list<class C<Xi,Yi>>; 
}; 


using result_t = cart_prod<foo,int_vals,char_vals>::type::applied_to<bar>; 
using ref_t = bar< 
    foo<1, -3>, foo<1, 3>, 
    foo<5, -3>, foo<5, 3>, 
    foo<7, -3>, foo<7, 3> 
>; 

static_assert(std::is_same<result_t, ref_t >::value, ""); 
0

kolejny (ale krócej) rozwiązaniem może być

template<typename Ret,typename R> 
auto magic(bar<u_list<>, R>, Ret result, R) { return result; } 

template<int I, int... Ints, typename... Foos, typename R> 
auto magic(bar<u_list<I,Ints...>, c_list<>>, bar<Foos...>, R rollback) { return magic(
    bar<u_list<Ints...>,R>{}, bar<Foos...>{}, rollback);} 

template<int I, int... Ints, char J, char ... Chars, typename... Foos, typename R > 
auto magic(bar<u_list<I,Ints...>, c_list<J,Chars...>>, bar<Foos...>, R rollback) { return magic(
    bar<u_list<I,Ints...>, c_list<Chars...>>{}, 
    bar<Foos...,foo<I,J>>{}, 
    rollback);} 

using result_t = decltype(magic(bar<int_vals,char_vals>{}, bar<>{}, char_vals{}));