Create a Cartesian product extension of two variational, non-pig parameter templates

Let's say i have

  • two lists of template parameters of a non-type type (which may have a different type)
  • a foo pattern that takes one value from each of these lists as a parameter

How to create a package of variational parameters foo s, parameterized by the Cartesian product of two list items?

Here is what I mean:

 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, ""); 

I am looking for a solution that works in C ++ 11 and does not use any libraries except the standard C ++ 11 library. I also have my own version of C ++ 14 index_sequence / make_index_sequence and can provide lists of non-type parameters in the form of arrays if it simplifies the code.

The closest I have found so far is the following: How to create a Cartesian product of a list of types? . Therefore, in principle (I did not test it), it should be possible to turn non-type parameter packets into type parameter packets, and then apply the solution in a related message, but I was hoping there was a simpler / shorter solution along the line of this:

 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{})); 
+5
source share
6 answers

You can do something like the following:

 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

Possible implementation of the std part:

 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...> {}; 

And change the answer:

 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

+2
source

Performing metaprogramming of templates in the field of pure types is much easier, in my opinion.

It takes some work to go from the land of non-piggy type template parameters to the land of types and vice versa, but this means that you use general metaprogramming utilities instead of those specific to your problem.


So, I will reduce your problem to a Cartesian product in the type list.

Here is my types package:

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

First we write fmap . Fmap takes a function and a list and returns a list of each element of the list with the function applied.

 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>...> {}; 

and fapply . fapply also performs a function and a list, but applies this function to the entire set of list items.

 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...>; }; 

As it happens, a partial fapply application fapply very useful:

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

We want you to be able to concatenate lists:

 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; 

Then here is 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...> >... > > > {}; 

Types specific to your problem:

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

A tool that lists lists of values ​​by type:

 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 takes a pair of types and converts them to foo:

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

now we put them together:

 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, ""); 

and Bob is your uncle.

cat , fmap and fapply are relatively standard functions in functional programming. applier simply allows you to write pattern matching functions by taking items instead of lists (this is partly fapply applied).

Living example .


Now, remember how I said that metaprogramming a template was easier with types?

Pay attention to all the template template options? It becomes easier if they are types.

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

and you can completely switch to hana-style metaprogramming with type tags and operator() on ztemplate and other entertainments.

+2
source

Taking cross product of list type as a base

 #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 {}; 

Deploy char... package with 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...>; }; 

We want the concat specialization to concat out of the base case.

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

Base case: no more ints

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

Recursive case: int followed by ints package

 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
source

Below are my 2 cents ...

If you want a general solution, the big problem that I see is that it is not easy (from C ++ 11; easier to C ++ 17) to extract the types of contained values ​​( int and char ) from int_vals and char_vals types.

Therefore, I assume that you should pass them to magic<> along with foo and bar (unless you want foo and bar be hard-coded).

So, calling magic<> will become (in my way)

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

Below is a complete working example of my solution.

 #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, ""); } 

Obviously, if you prefer some types of hard code ( u_list , c_list , foo and bar ), the solution becomes much simpler.

 #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
source

Same as others in 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
source

another (but shorter) solution could be

 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{} )); 
0
source

Source: https://habr.com/ru/post/1272748/


All Articles