vendredi 27 février 2015

Inserting any number of types into a pack of template arguments

InsertTypes<Pack, P<Ts...>, Is...>::type is Pack with the types Ts... inserted in positions Is..., respectively. For example,



InsertTypes<Pack<int, double, char, long, int>, Pack<short, float, std::string>, 2,4,1>::type,


is



Pack<int, std::string, double, short, char, long, float, int


(short inserted between double and char, float inserted between long and int, and std::string inserted between int and double).


My method: Sort the Is... in reverse order first (from largest to smallest), and apply Insert for each type in Ts... and each int in Is... Why reverse sort the Is...? Because if they are not in that order, inserting a type will bump the positions off by one and mess up the other insertions. But there is a flaw in my plan, which I'll explain shortly. First let me provide the helper functions I've written, which I tested to work correctly on their own:


Insert<T, P<Types...>, N>::type is the pack P<Types...> with T inserted in position N.



template <typename, typename, typename, int> struct InsertHelper;

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

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

template <typename, typename, int> struct Insert;

template <typename T, template <typename...> class P, typename... Types, int N>
struct Insert<T, P<Types...>, N> : InsertHelper<T, P<Types...>, P<>, N> {};


Now ReverseSortIntSequence (using quicksort):



template <int I, int J>
struct IntLessThan : std::conditional<(I < J), std::true_type, std::false_type>::type {};

template <int, typename> struct PrependInt;

template <int N, template <int...> class Z, int... Is>
struct PrependInt<N, Z<Is...>> {
using type = Z<N, Is...>;
};

template <template<int> class, typename> struct FilterInts;

template <template<int> class F, template <int...> class Z, int I, int... Is>
struct FilterInts<F, Z<I, Is...>> {
using type = typename std::conditional<F<I>::value,
typename PrependInt<I, typename FilterInts<F, Z<Is...>>::type>::type,
typename FilterInts<F, Z<Is...>>::type
>::type;
};

template <template<int> class F, template <int...> class Z>
struct FilterInts<F, Z<>> {
using type = Z<>;
};

template <typename, typename> struct MergeIntSequences;

template <template <int...> class Z, int... Is, int... Js>
struct MergeIntSequences<Z<Is...>, Z<Js...>> {
using type = Z<Is..., Js...>;
};

template <typename> struct ReverseSortIntSequence;

template <template <int...> class Z, int N, int... Is>
struct ReverseSortIntSequence<Z<N, Is...>> {
template<int I> struct less_than : std::integral_constant<bool, (I >= N)> {};
template <int I> struct more_than : std::integral_constant<bool, (I < N)> {};
using subsequence_less_than_N = typename FilterInts<less_than, Z<Is...>>::type;
using subsequence_more_than_N = typename FilterInts<more_than, Z<Is...>>::type;
using type = typename MergeIntSequences<typename ReverseSortIntSequence<subsequence_less_than_N>::type,
typename PrependInt<N, typename ReverseSortIntSequence<subsequence_more_than_N>::type>::type
>::type;
};

template<template <int...> class Z>
struct ReverseSortIntSequence<Z<>> {
using type = Z<>;
};


Now InsertTypes itself:



template <typename, typename, typename> struct InsertTypesHelper;

template <typename Pack, template <typename...> class P, template <int...> class Z>
struct InsertTypesHelper<Pack, P<>, Z<>> {
using type = Pack;
};

template <typename Pack, template <typename...> class P, typename First, typename... Rest, template <int...> class Z, int N, int... Ns>
struct InsertTypesHelper<Pack, P<First, Rest...>, Z<N, Ns...>> : InsertTypesHelper<typename Insert<First, Pack, N>::type, P<Rest...>, Z<Ns...>> {};

template <typename, typename, int...> struct InsertTypes;

template <typename Pack, template <typename...> class P, typename... Types, int... Is>
struct InsertTypes<Pack, P<Types...>, Is...> : InsertTypesHelper<Pack, P<Types...>, typename ReverseSortIntSequence<index_sequence<Is...>>::type> {};


Now, my tests:



int main() {
std::cout << std::is_same<
typename ReverseSortIntSequence<index_sequence<5,10,8,4,0,2,1,2,7,8,3>>::type,
index_sequence<10,8,8,7,5,4,3,2,2,1,0>
>::value << std::endl; // true

std::cout << std::is_same<
InsertTypesHelper<Pack<int, double, char, long, int>, Pack<float, short, std::string>, index_sequence<4,2,1>>::type,
Pack<int, std::string, double, short, char, long, float, int>
>::value << std::endl; // true (*)

std::cout << std::is_same<
typename ReverseSortIntSequence<index_sequence<2,4,1>>::type,
index_sequence<4,2,1>
>::value << std::endl; // true (**)

std::cout << std::is_same<
InsertTypes<Pack<int, double, char, long, int>, Pack<short, float, std::string>, 2,4,1>::type,
Pack<int, std::string, double, short, char, long, float, int>
>::value << std::endl; // false (rats!)
}


I get false above because despite (*) and (**) being true above, we must have Pack<short, float, std::string> permuted in the same way 2,4,1 is permuted in order to get that in reverse sorted order. I could proceed with this fix, but now it is getting overboard. I will still go ahead with that, but I seriously suspect there is a better method altogether, probably fairly short too. Any good ideas here?


Aucun commentaire:

Enregistrer un commentaire