3

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...>> {};
prestokeys
  • 4,817
  • 3
  • 20
  • 43
  • It would probably make things easier if you require the list of positions to be in ascending order. I.e. `1, 2, 4` instead of `2, 4, 1`. – jrok Feb 27 '15 at 13:17
  • Instead of sorting the indexes, have you considered just adding 1 to those greater than the current index when passing them to the next instantiation? – Dark Falcon Feb 27 '15 at 14:05
  • @jrok. Then it would be too easy: reverse the indices, and then reverse the pack of inserted types. The challenge I'm relishing in is to force no restrictions on the user input. To Dark Falcon, we only add 1 if the indices are ascending. If they are going up and down and up and down, etc..., then it is not that simple anymore. – prestokeys Feb 27 '15 at 15:18
  • I don't get why doing it from largest to smallest gives the "right" answer. Afterwards, the type associated with 4 is not in the 4th location in the type list. I guess it is after what was the 4th element in the original list? What do you do if there are multiple types inserted at spot 2? There is no clear correct answer, just an arbitrary one, which is a sign that your design is flawed. – Yakk - Adam Nevraumont Feb 27 '15 at 21:41
  • 1
    Thought: map `types` to `types...>`. Then insert without worrying about bumps. Then flatten. – Yakk - Adam Nevraumont Feb 27 '15 at 22:26
  • Yes! That's a wonderful idea! I'll get on that now! – prestokeys Feb 27 '15 at 22:33

2 Answers2

1

This is thanks to Yakk's quick suggestion above. Now we have a nice elegant solution!

#include <iostream>
#include <string>

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

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

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

template <typename, typename, int> struct InsertIntoPackOfPacks;

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

template <typename, typename>
struct FlattenPackHelper;

template <template <typename...> class P, template <typename...> class P2, typename... Ts, typename... Us, typename... Rest>
struct FlattenPackHelper<P<Ts...>, P<P2<Us...>, Rest...>> : FlattenPackHelper<P<Ts...>, P<Us..., Rest...>> {};

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

template <template <typename...> class P, typename... Ts, typename... Rest>
struct FlattenPackHelper<P<Ts...>, P<std::string, Rest...>> : FlattenPackHelper<P<Ts..., std::string>, P<Rest...>> {};

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

template <typename T> struct FlattenPack;

template <template <typename...> class P, typename... Ts>
struct FlattenPack<P<Ts...>> : FlattenPackHelper<P<>, P<Ts...>> {};

template <typename, typename, typename> struct InsertTypesHelper;

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

template <typename PackOfPacks, template <typename...> class P, typename First, typename... Rest, template <int...> class Z, int I, int... Is>
struct InsertTypesHelper<PackOfPacks, P<First, Rest...>, Z<I, Is...>> :
    InsertTypesHelper<typename InsertIntoPackOfPacks<First, PackOfPacks, I>::type, P<Rest...>, Z<Is...>> {};

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

template <int...> struct index_sequence {};

template <template <typename...> class P, typename... Types, typename... Ts, int... Is>
struct InsertTypes<P<Types...>, P<Ts...>, Is...> {
    using type = typename FlattenPack<typename InsertTypesHelper<P<P<Types>...>, P<Ts...>, index_sequence<Is...>>::type>::type;
};

// Testing
template <typename...> struct Pack {};

int main() {
    std::cout << std::boolalpha << 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;  // true
}

And for what it's worth, here is the comparison to my original, atrociously long, solution outlined in my original question:

#include <iostream>
#include <string>

// 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 <int I, int J>
struct IntLessThan : std::conditional<(I < J), std::true_type, std::false_type>::type {};

template <typename, template <int, int> class = IntLessThan> struct SortIntSequence;  

template <template <int...> class Z, int N, int... Is, template <int, int> class Comparator>  
struct SortIntSequence<Z<N, Is...>, Comparator> {  
    template<int I> struct less_than : std::integral_constant<bool, Comparator<I,N>::value> {};
    template <int I> struct more_than : std::integral_constant<bool, Comparator<N,I>::value || 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 SortIntSequence<subsequence_less_than_N, Comparator>::type,  
        typename PrependInt<N, typename SortIntSequence<subsequence_more_than_N, Comparator>::type>::type 
    >::type;
};

template<template <int...> class Z, template <int, int> class Comparator>  
struct SortIntSequence<Z<>, Comparator> {  
    using type = Z<>;  
};

// Given Pack, Z<Is...>, Z<Js...>, rearrange Pack in the same way that Z<Is...> is rearranged to get Z<Js...>.  PermutePack will take care of this.
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...>> {};

// Now InsertTypes itself.
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> {};

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 Pack1, typename Pack2, typename Sequence, typename ReverseSequence>
using InsertTypesHelperAlias = InsertTypesHelper<Pack1, typename PermutePack<Pack2, Sequence, ReverseSequence>::type, ReverseSequence>;

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

template <int...> struct index_sequence {};

template <typename Pack, template <typename...> class P, typename... Types, int... Is>
struct InsertTypes<Pack, P<Types...>, Is...> : InsertTypesHelperAlias<Pack, P<Types...>, index_sequence<Is...>, typename SortIntSequence<index_sequence<Is...>, IntGreaterThan>::type> {};  // SortIntSequence<index_sequence<Is...>, IntGreaterThan>::type is the reverse sort of index_sequence<Is...>.

// Testing
template <typename...> struct Pack {};

int main() {
    std::cout << std::boolalpha << 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;  // true 
}
prestokeys
  • 4,817
  • 3
  • 20
  • 43
  • 1
    If you write the insertion (first partial specialization) as `template class P, typename... First, typename... Rest, typename... Accumulated> struct InsertIntoPackOfPacksHelper, Rest...>, P, 0> { using type = P, Rest...>; };`, then you can also support stable insertion at the same index. – dyp Feb 28 '15 at 13:55
  • Yes. I made a further change yesterday so that `InsertTypes, Pack, 2,4,2,1,2>::type` gives `Pack`, but I'm not sure if that is what the result should be because it is confusing what multiple insertions into the same index should mean. My partial specialization was `struct InsertIntoPackOfPacksHelper, Others...>, P, 0> { using type = P, Others...>; };` – prestokeys Feb 28 '15 at 14:18
  • I don't quite see why `First` is needed in your partial specialization. For multiple insertions at the same index, as I said, I'd recommend *stability*, i.e., `Pack` (Hint: using array types for the examples might improve readability, e.g. `InsertTypes< Pack, Pack, 2,1,3 >` with `struct orig {}; struct insert {};`) – dyp Feb 28 '15 at 14:50
1

There is a way to do this using boost::mpl, which I think is a little easier to digest(?) I added a lot of comments to explain the steps, and appropriate indentation to make the code a little clearer... may be it helps...

#include <iostream>
#include <type_traits>

#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/sort.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/insert.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/transform.hpp>

using namespace boost::mpl;

template <typename R, typename It, typename End>
struct type_inserter
{ 
  // index to insert
  typedef typename deref<It>::type::first KType;
  // type to insert
  typedef typename deref<It>::type::second VType;
  // recurse
  typedef typename type_inserter<
    typename insert<
      R,
      // calculate the location based on the index
      typename advance<typename begin<R>::type, KType>::type,
      VType
    >::type,
    typename next<It>::type,
    End
  >::type type;
};

template <typename R, typename End>
struct type_inserter<R, End, End>
{
  typedef R type;
};

template <typename P>
struct index_access
{
  typedef typename P::first type;
};

struct make_pair
{
  template<typename Kv, typename Pv>
  struct apply
  {
    typedef pair<Kv, Pv> type;
  };
};

template <typename P1, typename P2, int ...I>
struct insert_pack
{
  // transform P2 and indexes
  static_assert(sizeof...(I) == size<P2>::value, "indexes/count P2 mismatch");
  // this iterates through both sequences, constructing a pair and inserting into a vector
  typedef typename transform<vector_c<int, I...>, P2, make_pair, back_inserter<vector0<>>>::type entries;
  // sort the sequence by the index..
  typedef typename sort<entries, greater<index_access<_1>, index_access<_2>>>::type reversed;
  // once sorted, insert the sorted range into the main vector using custom inserter
  typedef typename type_inserter<P1, typename begin<reversed>::type, typename end<reversed>::type>::type type;
};

int main()
{
  typedef vector<int, double, char, long, int> pack1;
  typedef vector<short, float, std::string> pack2;
  // this combines the pack
  typedef insert_pack<pack1, pack2, 2, 4, 1>::type combined;
  // this is what we expect
  typedef vector<int, std::string, double, short, char, long, float, int> packs;
  // sanity check
  BOOST_MPL_ASSERT(( equal< packs, combined> ));
}
Nim
  • 33,299
  • 2
  • 62
  • 101
  • Not familiar with Boost yet, but when I ran your code with `typedef insert_pack::type combined;` I get the result to be `vector`, which is what my program gives too (the types all inserted in position 2, which forces them to appear in reverse order). However, dyp above states that stable insertion should appear in non-reversed order. It's just a matter of taste, I guess? – prestokeys Mar 01 '15 at 02:33
  • I don't know if I agree with that, the sequence of 2, 2, 2 indicates that each entry as you process must be inserted at position 2 with the state of the sequence at that point in time, I don't see how it means insert this range at position 2 (to preserve order,) I think to preserve, you need to support a range insertion - which is a different (but easier problem..) – Nim Mar 02 '15 at 10:28
  • ...If you took a standard vector and inserted three entries at position 2, you will face a similar reversed sequence, it's effectively the expected behaviour of the insert operation (it's standalone operating on the sequence at that point..) I don't know if that makes it any clearer.. – Nim Mar 02 '15 at 10:30