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, 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? I thought of extracting the pairs of types that the indices determine (the inserted types will go between the pairs), but this won't work if there are duplicate types in the original pack (and also because of the types being inserted too).
Update: I finished the permutation helper discussed above, and now everything is working correctly. But there must be a better and shorter solution than all this mess.
template <int, typename> struct NthType;
template <int N, template <typename...> class P, typename First, typename... Rest>
struct NthType<N, P<First, Rest...>> : NthType<N-1, P<Rest...>> {};
template <template <typename...> class P, typename First, typename... Rest>
struct NthType<0, P<First, Rest...>> {
using type = First;
};
template <int, int, typename> struct FindIndexOfIntHelper;
template <int N, int FindMe, template <int...> class Z, int... Rest>
struct FindIndexOfIntHelper<N, FindMe, Z<FindMe, Rest...>> : std::integral_constant<int, N> {};
template <int N, int FindMe, template <int...> class Z>
struct FindIndexOfIntHelper<N, FindMe, Z<>> : std::integral_constant<int, -1> {}; // Not found.
template <int N, int FindMe, template <int...> class Z, int First, int... Rest>
struct FindIndexOfIntHelper<N, FindMe, Z<First, Rest...>> : FindIndexOfIntHelper<N+1, FindMe, Z<Rest...>> {};
template <int FindMe, typename Pack>
using FindIndexOfInt = FindIndexOfIntHelper<0, FindMe, Pack>;
template <typename, typename, typename, typename> struct PermutePackHelper;
template <typename Pack, template <typename...> class P, typename... Accumulated, typename IndicesPack, template <int...> class Z>
struct PermutePackHelper<Pack, P<Accumulated...>, IndicesPack, Z<>> {
using type = P<Accumulated...>;
};
template <typename Pack, template <typename...> class P, typename... Accumulated, typename IndicesPack, template <int...> class Z, int I, int... Is>
struct PermutePackHelper<Pack, P<Accumulated...>, IndicesPack, Z<I, Is...>> :
PermutePackHelper<Pack, P<Accumulated..., typename NthType<FindIndexOfInt<I, IndicesPack>::value, Pack>::type>,
IndicesPack, Z<Is...>> {};
template <typename, typename, typename> struct PermutePack;
template <template <typename...> class P, typename... Types, template <int...> class Z, int... Is, int... Js>
struct PermutePack<P<Types...>, Z<Is...>, Z<Js...>> : PermutePackHelper<P<Types...>, P<>, Z<Is...>, Z<Js...>> {};