For fun I gave it a try to make a manual implementation of this work with variadic templates, std::integer_sequence and std::tuple_cat and was actually surprised to get it to work quite easily: Based on simple arrays of arbitrary length
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
and given the variadic template classes (e.g. Foo and Bar) it generates all possible permutations itself and merges them to an std::tuple (or boost::mpl::list) data type for which a convenient alias can be defined:
using SomeAlias = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
Not being familiar with Boost::MPL I tried to stay away from it: My first version is actually based on std::tuple
Try std::tuple version here!
If you want to have a boost::mpl::list instead of an std::tuple this can be easily done with another variadic template conversion function
Try boost::mpl::list version here!
The next section will go into more detail on how this can be achieved!
For this purpose I wrote a class that - based on a few template arguments such as the said combinations of the int and char parameters as well as the corresponding template template class - creates an std::tuple containing all permutations of the int and char arrays of this single template template class. This is done by creating two permutation vectors holding the pairwise permutations. E.g. two input arrays t1_in = {1, 2} and t2_in = {'a', 'b', 'c'}; are expanded to t1 = {1, 1, 1, 2, 2, 2} and t2 = {'a', 'b', 'c', 'a', 'b', 'c'} with the function duplicateArray and then an std::integer_sequence is created so the two can be fused into a template T<t1[I], t2[I]> that in combination with the std::integer_sequence gives you all permutations for a single class:
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1_in, std::array<char,I2> const& t2_in, template <int, char> class T>
class SingleClassAllPermutations {
private:
template <std::size_t K, std::size_t I, typename TA, std::size_t J>
static constexpr auto duplicateArray(std::array<TA, J> const& arr_in) {
std::array<TA, I*J*K> arr_out {0};
std::size_t l {0};
for (std::size_t i = 0; i < I; ++i) {
for (std::size_t j = 0; j < J; ++j) {
for (std::size_t k = 0; k < K; ++k) {
arr_out[l] = arr_in[j];
++l;
}
}
}
return arr_out;
}
static constexpr std::size_t N = I1*I2;
static constexpr std::array<int,N> t1 = duplicateArray<I2,1>(t1_in);
static constexpr std::array<char,N> t2 = duplicateArray<1,I1>(t2_in);
static constexpr auto index_seq = std::make_index_sequence<N>{};
template <std::size_t... I>
static constexpr auto getTuple(std::index_sequence<I...>) {
return std::make_tuple(T<t1[I], t2[I]>() ...);
}
public:
static constexpr auto tup = getTuple(index_seq);
};
Then I created a variadic template which may take several different template template classes (as long as their template arguments are int and char respectively) as additional input arguments and then merges the internal tuple tup of the individual permutations with std::tuple_cat to create the tuple containing all possible permutations of the int and char arrays as well as the different variadic template template classes:
template <std::size_t I1, std::size_t I2, std::array<int,I1> const& t1, std::array<char,I2> const& t2, template <int,char> class... Ts>
class AllClassesAllPermutations {
public:
static constexpr auto getTuple() {
return std::tuple_cat(SingleClassAllPermutations<I1,I2,t1,t2,Ts>::tup ...);
}
using type = decltype(getTuple());
};
Now one can then define global constexpr arrays inside namespaces and a convenient alias such as
namespace some_namespace {
constexpr std::array<int,2> t1 = {1, 2};
constexpr std::array<char,3> t2 = {'a', 'b', 'c'};
using SomeInstantiation = typename AllClassesAllPermutations<t1.size(), t2.size(), t1, t2, Foo, Bar>::type;
}
This way one can re-use the templates above to generate different data types.
The templates then expand it to all possible permutations, in the case above to
std::tuple<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
Finally if you want to have an boost::mpl::list instead you can introduce a conversion function such as
template <class... Ts>
static constexpr auto tupleToMplList(std::tuple<Ts...>) {
return boost::mpl::list<Ts...>{};
}
that you again use to decltype instead of the std::tuple which results in a data type
boost::mpl::list<Foo<1,'a'>, Foo<1,'b'>, Foo<1,'c'>, Foo<2,'a'>, Foo<2,'b'>, Foo<2,'c'>,
Bar<1,'a'>, Bar<1,'b'>, Bar<1,'c'>, Bar<2,'a'>, Bar<2,'b'>, Bar<2,'c'>>
Try it here