mardi 27 janvier 2015

Variadic templates: Interlacing multiple packs

I thought of this question while driving home: given any number of packs, take the first type from each pack, put them together. Then the second type from each pack, put them together, etc... Then merge them all. Any leftoevers will repeat the process among themselves. For example, using integers to represent different types for better readability,



InterlacePacks<Pack<1 2 3 4>, Pack<5 6 7>, Pack<8 9 10 11 12>>::type


will give



Pack<1 5 8 2 6 9 3 7 10 4 11 12>


The following code works if all the packs are the same sizes only. I'm now totally stuck dealing with the "left-overs" when the packs are different sizes. Here is my code so far. I explain each phase so that you know what my plan was:



#include <iostream>
#include <typeinfo>

// First a helper to remove the first N types from a pack:
template <int, typename> struct RemoveHead;

template <typename Pack>
struct RemoveHead<0, Pack> { using type = Pack; };

template <template <typename...> class P, typename First, typename... Rest>
struct RemoveHead<0, P<First, Rest...>> { using type = P<First, Rest...>; };

template <int N, template <typename...> class P, typename First, typename... Rest>
struct RemoveHead<N, P<First, Rest...>> : RemoveHead<N-1, P<Rest...>> {};

// Now a helper to merge multiple packs:
template <typename...> struct MergePacks;

template <typename Pack>
struct MergePacks<Pack> {
using type = Pack;
};

// Final Pack type shall be the first one listed, if there are different pack types.
template <template <typename...> class P1, template <typename...> class P2, typename... Types1, typename... Types2, typename... Packs>
struct MergePacks<P1<Types1...>, P2<Types2...>, Packs...> : MergePacks<P1<Types1..., Types2...>, Packs...> {};

// First collect the first type from each pack:
template <typename, typename...> struct InterlacePacksHelper1;

template <template <typename...> class P, typename... Ts>
struct InterlacePacksHelper1<P<Ts...>> { using type = P<Ts...>; };

template <template <typename...> class P, template <typename...> class FirstPack, typename... Ts, typename First, typename... Rest, typename... Packs>
struct InterlacePacksHelper1<P<Ts...>, FirstPack<First, Rest...>, Packs...> : InterlacePacksHelper1<P<Ts..., First>, Packs...> {};

// Now remove the first type from each pack and repeat the process. Use a parameter N as a counter, where N will start as the minimum size of the packs.
template <int, typename, typename...> struct InterlacePacksHelper;

template <template <typename...> class P, typename... Ts, typename... Packs>
struct InterlacePacksHelper<0, P<Ts...>, Packs...> { using type = P<Ts...>; };

template <int N, template <typename...> class P, typename... Ts, typename... Packs>
struct InterlacePacksHelper<N, P<Ts...>, Packs...> : InterlacePacksHelper<N-1,
typename MergePacks<P<Ts...>, typename InterlacePacksHelper1<P<>, Packs...>::type>::type,
typename RemoveHead<1, Packs>::type...> {};

// Now obtain the smallest pack size, given a list of packs.
template <int N, typename...> struct MinPackSize;

template <int N>
struct MinPackSize<N> : std::integral_constant<int, N> {};

template <int N, template <typename...> class P, typename... Types, typename... Packs>
struct MinPackSize<N, P<Types...>, Packs...> : std::integral_constant<int,
(sizeof...(Types) < N) ? sizeof...(Types) : N> {};

// Finally, InterlacePacks itself.
template <typename...> struct InterlacePacks;

template <template <typename...> class P, typename... Ts, typename... Packs>
struct InterlacePacks<P<Ts...>, Packs...> : InterlacePacksHelper<MinPackSize<sizeof...(Ts), Packs...>::value, P<>, P<Ts...>, Packs...> {};

// test ----------------------------------------------------------------
template <typename...> struct Pack {};
template <typename...> struct Group {};
template <typename...> struct Wrap {};
struct Object {}; struct Blob {};

int main() {
using TestPack1 = Pack<int, double, Object>; // 3 types
using TestPack2 = Group<double, std::string, int, short, long>; // 5 types
using TestPack3 = Wrap<char, short, Blob, std::string>; // 4 types
InterlacePacks<TestPack1, TestPack2, TestPack3>::type interlacedPack;
std::cout << std::boolalpha << std::is_same< decltype(interlacedPack),
Pack<int, double, char, double, std::string, short, Object, int, Blob> >::value << std::endl; // true
// Want it to be Pack<int, double, char, double, std::string, short, Object, int, Blob, short, std::string, long>
}


So how to fix the code so that the desired type is



Pack<int, double, char, double, std::string, short, Object, int, Blob, short, std::string, long>


instead?


Aucun commentaire:

Enregistrer un commentaire