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