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 class
es:
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