Self explanatory.
Basically, say I have type lists like so:
using type_list_1 = type_list<int, somestructA>;
using type_list_2 = type_list<somestructB>;
using type_list_3 = type_list<double, short>;
They can be variadic number of type lists.
How do I get a typelist of Cartesian product?
result = type_list<
type_list<int, somestructB, double>,
type_list<int, somestructB, short>,
type_list<somestructA, somestructB, double>,
type_list<somestructA, somestructB, short>
>;
I did dabble on how to create a two-way Cartesian product as given here: How to create the Cartesian product of a type list?, but n way seems to be no so trivial.
For now I am trying...
template <typename...> struct type_list{};
// To concatenate
template <typename... Ts, typename... Us>
constexpr auto operator|(type_list<Ts...>, type_list<Us...>) {
return type_list{Ts{}..., Us{}...};
}
template <typename T, typename... Ts, typename... Us>
constexpr auto cross_product_two(type_list<T, Ts...>, type_list<Us...>) {
return (type_list<type_list<T,Us>...>{} | ... | type_list<type_list<Ts, Us>...>{});
}
template <typename T, typename U, typename... Ts>
constexpr auto cross_product_impl() {
if constexpr(sizeof...(Ts) >0) {
return cross_product_impl<decltype(cross_product_two(T{}, U{})), Ts...>();
} else {
return cross_product_two(T{}, U{});
}
}
I will just say that considering how difficult it is to get it right, just use boost as in the answer by Barry. Unfortunately I have to be stuck with a hand rolled approach because to use boost or not is a decision that comes from somewhere else :(
With Boost.Mp11, this is a short one-liner (as always):
using result = mp_product<
type_list,
type_list_1, type_list_2, type_list_3>;
Demo.
Ok, got it. It's not pretty but it works:
template<class ... T>
struct type_list{};
struct somestructA{};
struct somestructB{};
using type_list_1 = type_list<int, somestructA, char>;
using type_list_2 = type_list<somestructB>;
using type_list_3 = type_list<double, short, float>;
template<class TL1, class TL2>
struct add;
template<class ... T1s, class ... T2s>
struct add<type_list<T1s...>, type_list<T2s...>>
{
using type = type_list<T1s..., T2s...>;
};
template<class ... TL>
struct concat;
template<class TL, class ... TLs>
struct concat<TL, TLs...>
{
using type = typename add<TL, typename concat<TLs...>::type>::type;
};
template<class TL>
struct concat<TL>
{
using type = TL;
};
static_assert(std::is_same_v<type_list<int, somestructA, char, double, short, float>, typename add<type_list_1, type_list_3>::type>);
template<class TL1, class TL2>
struct multiply_one;
// Prepends each element of T1 to the list T2.
template<class ... T1s, class ... T2s>
struct multiply_one<type_list<T1s...>, type_list<T2s...>>
{
using type = typename concat<type_list<type_list<T1s, T2s...>...>>::type;
};
static_assert(std::is_same_v<
type_list<
type_list<int, double, short, float>,
type_list<somestructA, double, short, float>,
type_list<char, double, short, float>
>,
typename multiply_one<type_list_1, type_list_3>::type>);
// Prepends each element of TL to all type lists in TLL.
template<class TL, class TLL>
struct multiply_all;
template<class TL, class ... TLs>
struct multiply_all<TL, type_list<TLs...>>
{
using type = typename concat<typename multiply_one<TL, TLs>::type...>::type;
};
static_assert(std::is_same_v<
type_list<
type_list<int, double, short, float>,
type_list<somestructA, double, short, float>,
type_list<char, double, short, float>
>,
typename multiply_all<type_list_1, type_list<type_list_3>>::type>);
static_assert(std::is_same_v<
type_list<
type_list<int, somestructB>,
type_list<somestructA, somestructB>,
type_list<char, somestructB>,
type_list<int, double, short, float>,
type_list<somestructA, double, short, float>,
type_list<char, double, short, float>
>,
typename multiply_all<type_list_1, type_list<type_list_2, type_list_3>>::type>);
template<class TL, class ... TLs>
struct cartesian_product
{
using type = typename multiply_all<TL, typename cartesian_product<TLs...>::type>::type;
};
template<class ... Ts>
struct cartesian_product<type_list<Ts...>>
{
using type = type_list<type_list<Ts>...>;
};
using expected_result = type_list<
type_list<int, somestructB, double>,
type_list<somestructA, somestructB, double>,
type_list<char, somestructB, double>,
type_list<int, somestructB, short>,
type_list<somestructA, somestructB, short>,
type_list<char, somestructB, short>,
type_list<int, somestructB, float>,
type_list<somestructA, somestructB, float>,
type_list<char, somestructB, float>
>;
static_assert(std::is_same_v<expected_result,
cartesian_product<type_list_1, type_list_2, type_list_3>::type>);
https://godbolt.org/z/L5eamT
I left my own static_assert
tests in there for... Well, I hope they help.
Also, I'm sure there has to be a nicer solution. But this was the obvious "I know this will eventually lead to the goal" path. I eventually had to resort to adding a concat
or sorts, I'm sure that it could be used much earlier to skip most of the cruft.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With