This is a very tough one (for me at least). I'll start off by discussing an easier task that I've already solved. ExpandPacks<Packs...>::type
is a pack of all packs obtained from taking one type from each pack in Packs...
. For example
ExpandPacks<P<int, char>, P<bool, double, long>>::type
is
P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >
I've already written the code to do this for any number of packs:
#include <iostream>
#include <type_traits>
template <typename T, typename Pack> struct Prepend;
template <typename...> struct Merge;
template <typename T, template <typename...> class P, typename... Ts>
struct Prepend<T, P<Ts...>> {
using type = P<T, Ts...>;
};
template <typename Pack>
struct Merge<Pack> {
using type = Pack;
};
template <template <typename...> class P, typename... Ts, typename... Us>
struct Merge<P<Ts...>, P<Us...>> {
using type = P<Ts..., Us...>;
};
template <typename First, typename... Rest>
struct Merge<First, Rest...> : Merge<First, typename Merge<Rest...>::type> {};
template <typename... Packs> struct ExpandPacks;
template <typename T, typename Pack> struct ExpandPacksHelper;
template <typename T, typename PackOfPacks> struct ExpandPacksHelper2;
template <typename Pack, typename PackOfPacks> struct ExpandPacksHelper3;
template <template <typename...> class P, typename T, typename... Ts>
struct ExpandPacksHelper<T, P<Ts...>> {
using type = P<P<T, Ts>...>;
};
template <template <typename...> class P, typename T, typename... Packs>
struct ExpandPacksHelper2<T, P<Packs...>> {
using type = P<typename Prepend<T, Packs>::type...>;
};
template <template <typename...> class P, typename... Ts, typename... Packs>
struct ExpandPacksHelper3<P<Ts...>, P<Packs...>> : Merge<typename ExpandPacksHelper2<Ts, P<Packs...>>::type...> {};
template <template <typename...> class P, typename... Ts, typename Pack>
struct ExpandPacks<P<Ts...>, Pack> : Merge<typename ExpandPacksHelper<Ts, Pack>::type...> {};
template <typename First, typename... Rest>
struct ExpandPacks<First, Rest...> : ExpandPacksHelper3<First, typename ExpandPacks<Rest...>::type> {};
// Testing
template <typename...> struct P;
int main() {
std::cout << std::boolalpha << std::is_same<
typename ExpandPacks<P<int, char>, P<bool, double, long>>::type,
P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >
>::value << '\n'; // true
std::cout << std::is_same<
typename ExpandPacksHelper3<P<short, float>, P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >>::type,
P< P<short, int, bool>, P<short, int, double>, P<short, int, long>, P<short, char, bool>, P<short, char, double>, P<short, char, long>, P<float, int, bool>, P<float, int, double>, P<float, int, long>, P<float, char, bool>, P<float, char, double>, P<float, char, long> >
>::value << '\n'; // true
std::cout << std::is_same<
typename ExpandPacks<P<short, float>, P<int, char>, P<bool, double, long>>::type,
P< P<short, int, bool>, P<short, int, double>, P<short, int, long>, P<short, char, bool>, P<short, char, double>, P<short, char, long>, P<float, int, bool>, P<float, int, double>, P<float, int, long>, P<float, char, bool>, P<float, char, double>, P<float, char, long> >
>::value << '\n'; // true
std::cout << std::is_same<
typename ExpandPacks<P<int, bool>, P<short, float>, P<int, char>, P<bool, double, long>>::type,
P< P<int, short, int, bool>, P<int, short, int, double>, P<int, short, int, long>, P<int, short, char, bool>, P<int, short, char, double>, P<int, short, char, long>, P<int, float, int, bool>, P<int, float, int, double>, P<int, float, int, long>, P<int, float, char, bool>, P<int, float, char, double>, P<int, float, char, long>,
P<bool, short, int, bool>, P<bool, short, int, double>, P<bool, short, int, long>, P<bool, short, char, bool>, P<bool, short, char, double>, P<bool, short, char, long>, P<bool, float, int, bool>, P<bool, float, int, double>, P<bool, float, int, long>, P<bool, float, char, bool>, P<bool, float, char, double>, P<bool, float, char, long> >
>::value << '\n'; // true
}
But now I want to generalize this. Instead of always choosing just one type from each pack, one must choose N types from each pack, where N is a template parameter. If N exceeds the size of a certain pack, then simply take all the types from that pack. The order of the types from each pack shall be preserved. But I'm totally stuck here. Also, I'm not specifying any particular order of the outputted packs, which makes testing harder too. Here is an example:
ExpandPacks<2, P<int, char, short>, P<bool, double, long>>::type
is, up to order of the packs,
P< P<int, char, bool, double>, P<int, char, bool, long>, P<int, char, double, long>,
P<int, short, bool, double>, P<int, short, bool, long>, P<int, short, double, long>,
P<char, short, bool, double>, P<char, short, bool, long>, P<char, short, double, long> >
Packs of 4 obtained by taking 2 from P<int, char, short>
and 2 from P<bool, double, long>
. I could alternatively define
ExpandPacks<std::index_sequence<2,1>, P<int, char, short>, P<bool, double, long>>::type
to mean take 2 from P<int, char, short>
and 1 from P<bool, double, long>
, which should be an easy extension once the first problem is solved (or solve this one right away, which will solve both questions). Since the order of the outputted packs is not specified, I guess the easiest way to test the output is to check that
ExpandPacks<1, P<int, char>, P<bool, double, long>>::type
or alternatively,
ExpandPacks<std::index_sequence<1,1>, P<int, char>, P<bool, double, long>>::type
is
P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >
since that must reduce to the version I've already solved. I'll be willing to offer a bounty if no one can solve this so quickly. Thanks.
Update: For testing purposes, I've just written a program to check if two packs of types are equal to each other up to permutation of the types: http://ideone.com/zb7NA7 This may be of help in the tests since the output of the packs is not specified to be in any specific order here.