Aim:
Get a type alias to:
Foo<int, double, char, const char*>
using 2 parameter packs:
using Pack1 = Pack<int, char>;
using Pack2 = Pack<double, const char*>;
and then interleaving them:
typename ToFoo<Pack1, Pack2>::type
and performing a static_assert
that they are equivalent:
using T1 = Foo<int, double, char, const char*>;
using T2 = typename ToFoo<Pack<int, char>, Pack<double, const char*>>::type;
static_assert(std::is_same<T1, T2>::value, "passed");
Solution:
We can interleave 2 tuples into 1 as follows:
template <class Tuple1, class Tuple2, std::size_t ... indices>
auto interleave(Tuple1 t1, Tuple2 t2, std::integer_sequence<std::size_t, indices...>)
{
return std::tuple_cat(std::make_tuple(std::get<indices>(t1),
std::get<indices>(t2))...);
}
template <class Tuple1, class Tuple2>
auto interleave(Tuple1 t1, Tuple2 t2)
{
return interleave(t1, t2, std::make_index_sequence<std::tuple_size<Tuple1>::value>());
}
We can get the type of the resulting interleaved tuple as follows:
template<typename... Ts>
struct Pack
{
using type = std::tuple<Ts...>;
};
template<typename T0, typename T1>
struct Interleaved;
template<typename... P0, typename... P1>
struct Interleaved<Pack<P0...>, Pack<P1...>>
{
using Pack0 = typename Pack<P0...>::type;
using Pack1 = typename Pack<P1...>::type;
using type = decltype(interleave(std::declval<Pack0>(), std::declval<Pack1>()));
};
We can then take that std::tuple<Ts...>
type and "convert" it to Foo<Ts...>
as follows:
template<typename T>
struct TupleToFoo;
template<typename... Ts>
struct TupleToFoo<std::tuple<Ts...>>
{
using type = Foo<Ts...>;
};
Finally we wrap it all up an a helper class ToFoo
which takes 2 Packs
and defines a type alias:
template<typename T0, typename T1>
struct ToFoo;
template<typename... P0, typename... P1>
struct ToFoo<Pack<P0...>, Pack<P1...>>
{
using type = typename TupleToFoo<typename Interleaved<Pack<int, char>, Pack<double, const char*>>::type>::type;
};
Full working example: (coliru)
#include <tuple>
template <class Tuple1, class Tuple2, std::size_t ... indices>
auto interleave(Tuple1 t1, Tuple2 t2, std::integer_sequence<std::size_t, indices...>)
{
return std::tuple_cat(std::make_tuple(std::get<indices>(t1),
std::get<indices>(t2))...);
}
template <class Tuple1, class Tuple2>
auto interleave(Tuple1 t1, Tuple2 t2)
{
return interleave(t1, t2, std::make_index_sequence<std::tuple_size<Tuple1>::value>());
}
template<typename... Ts>
struct Pack
{
using type = std::tuple<Ts...>;
};
template<typename T0, typename T1>
struct Interleaved;
template<typename... P0, typename... P1>
struct Interleaved<Pack<P0...>, Pack<P1...>>
{
using Pack0 = typename Pack<P0...>::type;
using Pack1 = typename Pack<P1...>::type;
using type = decltype(interleave(std::declval<Pack0>(), std::declval<Pack1>()));
};
template<typename... Ts>
struct Foo
{};
template<typename T>
struct TupleToFoo;
template<typename... Ts>
struct TupleToFoo<std::tuple<Ts...>>
{
using type = Foo<Ts...>;
};
template<typename T0, typename T1>
struct ToFoo;
template<typename... P0, typename... P1>
struct ToFoo<Pack<P0...>, Pack<P1...>>
{
using type = typename TupleToFoo<typename Interleaved<Pack<int, char>, Pack<double, const char*>>::type>::type;
};
int main()
{
using T1 = Foo<int, double, char, const char*>;
using T2 = typename ToFoo<Pack<int, char>, Pack<double, const char*>>::type;
static_assert(std::is_same<T1, T2>::value, "passed");
return 0;
}