'N-ary cartesian product of variadic templates [duplicate]
I am trying to compute the n-ary cartesian product of variadic templates and so far I haven't found an elegant method for the n-ary case (unary and binary are easy, see below).
Here is the current status of the investigations. For convenience, I introduce a pack<Types...> to store variadic templates. And then some utility functions and helpers:
// Preamble
#include <iostream>
#include <type_traits>
// Helper function to display the result
template <class...> void print() {std::cout << __PRETTY_FUNCTION__ << "\n";}
// Pack to hold variadic templates
template <class...> struct pack {};
// Sum of packs
template <class...>
struct pack_sum;
// Sum of packs: single pack specialization
template <class... Types>
struct pack_sum<pack<Types...>> {
using type = pack<Types...>;
};
// Sum of packs: n-ary recursive specialization
template <class... Types1, class... Types2, class... Packs>
struct pack_sum<pack<Types1...>, pack<Types2...>, Packs...> {
using type = typename pack_sum<pack<Types1..., Types2...>, Packs...>::type;
};
Based on that, I can then introduction my cartesian-product (unary and binary versions):
// Cartesian product of packs
template <class...>
struct pack_product;
// Cartesian product of packs: single empty pack specialization
template <>
struct pack_product<pack<>> {
using type = pack<>;
};
// Cartesian product of packs: empty pack specialization
template <class... Types2, class... Packs>
struct pack_product<pack<>, pack<Types2...>, Packs...> {
using type = typename pack_product<pack<>, Packs...>::type;
};
// Cartesian product of packs: unary specialization
template <class... Types>
struct pack_product<pack<Types...>> {
using type = pack<pack<Types>...>;
};
// Cartesian product of packs: binary specialization
template <class Type1, class... Types1, class... Types2>
struct pack_product<pack<Type1, Types1...>, pack<Types2...>> {
using type = typename pack_sum<
pack<pack<Type1, Types2>...>,
typename pack_product<pack<Types1...>, pack<Types2...>>::type
>::type;
};
Which I can test with:
// Test
int main(int argc, char* argv[]) {
// Packs
using empty_pack = pack<>;
using char_pack = pack<signed char, char, unsigned char>;
using int_pack = pack<int, unsigned int>;
using float_pack = pack<float, double, long double>;
// Tests
print<typename pack_product<empty_pack>::type>();
print<typename pack_product<char_pack>::type>();
print<typename pack_product<empty_pack, char_pack>::type>();
print<typename pack_product<char_pack, empty_pack>::type>();
print<typename pack_product<char_pack, float_pack>::type>();
}
The whole functional code is here: https://godbolt.org/z/Pv15W95EK
The output of:
typename pack_product<
pack<signed char, char, unsigned char>,
pack<float, double, long double>
>::type
is:
pack<
pack<signed char, float>,
pack<signed char, double>,
pack<signed char, long double>,
pack<char, float>,
pack<char, double>,
pack<char, long double>,
pack<unsigned char, float>,
pack<unsigned char, double>,
pack<unsigned char, long double>
>
as expected.
Now, the PROBLEM:
I am looking for a n-ary version, but that avoids nested packs so that:
pack_product<pack<A1, A2>, pack<B1, B2>, pack<C1, C2>>
gives the following results:
pack<
pack<A1, B1, C1>, pack<A1, B1, C2>, pack<A1, B2, C1>, pack<A1, B2, C2>,
pack<A2, B1, C1>, pack<A2, B1, C2>, pack<A2, B2, C1>, pack<A2, B2, C2>
>
and NOT some kind of:
pack<
pack<A1, pack<B1, C1>>, pack<A1, pack<B1, C2>>, pack<A1, pack<B2, C1>>, pack<A1, pack<B2, C2>>,
pack<A2, pack<B1, C1>>, pack<A2, pack<B1, C2>>, pack<A2, pack<B2, C1>>, pack<A2, pack<B2, C2>>,
>
QUESTION: While searching, I found extremely ugly versions that seemed to work, but I would like something as clean and elegant as possible that minimizes the number of helper structs and specializations. If the n-ary specialization covers the binary version that's a plus, but I'm not sure that is possible. Any help would be extremely welcomed :)
NOTE: A possible specialization for the thing I'm looking for would be, but that is just a suggestion:
template <class Type1, class... Types1, class... Types2, class... Pack>
struct pack_product<pack<Type1, Types1...>, pack<Types2...>, Pack...> {
using type = /* THE THING I'M LOOKING FOR */
};
Solution 1:[1]
NOTE: A possible specialization for the thing I'm looking for would be, but that is just a suggestion:
For the n-ary version, you only need to recurse the binary version since pack_product<a, b, c> is equivalent to pack_product<pack_product<a, b>, c>
// Cartesian product of packs: n-ary specialization
template <class Type1, class... Types1, class... Types2, class... Pack>
struct pack_product<pack<Type1, Types1...>, pack<Types2...>, Pack...> {
using type = typename pack_product<typename pack_sum<
pack<pack<Type1, Types2>...>,
typename pack_product<pack<Types1...>, pack<Types2...>>::type
>::type, Pack...>::type;
};
However, this will generate
{pack<pack<pack<signed char, float>, int>, pack<pack<signed char, float>, unsigned int>, ...}
In order to remove nested packs, partial specialization can be performed on binary/n-arg specialization
// Cartesian product of packs: binary specialization for nested packs
template <class... Types, class... Types1, class... Types2>
struct pack_product<pack<pack<Types...>, Types1...>, pack<Types2...>> {
using type = typename pack_sum<
pack<pack<Types..., Types2>...>,
typename pack_product<pack<Types1...>, pack<Types2...>>::type
>::type;
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
