mercredi 6 avril 2016

Permutation P(N,R) of types in compile time

I've written a working code for computing P(N,R) for packs when the pack has all different types, e.g.

std::cout << std::is_same<
    PermutationN<2, P<int, char, bool>>,
    P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
>::value << '\n';  // true

But when there are repeat elements, I'm getting the wrong results. For example,

PermutationN<2, P<int, int, char>>

should be

P< P<int, int>, P<int, char>, P<char, int> >

Here is my working code for when all the types are different. I'm stuck on how to adapt it so that it gives correct results when there are repeated types in the pack. Any help would be appreciated.

#include <iostream>
#include <type_traits>

template <typename, typename> struct Merge;

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

template <std::size_t N, typename Pack, typename Previous, typename... Output> struct PermutationNHelper;

template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Prev, typename... Output>
struct PermutationNHelper<N, P<First, Rest...>, P<Prev...>, Output...> : Merge<
    typename PermutationNHelper<N-1, P<Prev..., Rest...>, P<>, Output..., First>::type,  // P<Prev..., Rest...> are the remaining elements, thus ensuring that the next element chosen will not be First.  The new Prev... is empty since we now start at the first element of P<Prev..., Rest...>.
    typename PermutationNHelper<N, P<Rest...>, P<Prev..., First>, Output...>::type  // Using P<Rest...> ensures that the next set of permutations will begin with the type after First, and thus the new Prev... is Prev..., First.
> {};

template <std::size_t N, template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<N, P<>, Previous, Output...> {
    using type = P<>;
};

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

template <template <typename...> class P, typename Previous, typename... Output>
struct PermutationNHelper<0, P<>, Previous, Output...> {
    using type = P<P<Output...>>;
};

template <typename Pack> struct EmptyPack;

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

template <std::size_t N, typename Pack>
using PermutationN = typename PermutationNHelper<N, Pack, typename EmptyPack<Pack>::type>::type;

// Testing
template <typename...> struct P;

int main() {
    std::cout << std::is_same<
        PermutationN<2, P<int, char, bool>>,
        P< P<int, char>, P<int, bool>, P<char, int>, P<char, bool>, P<bool, int>, P<bool, char> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        PermutationN<2, P<int, int, int>>,
        P< P<int, int>, P<int, int>, P<int, int>, P<int, int>, P<int, int>, P<int, int> >
    >::value << '\n';  // true (but the answer should be P< P<int, int> >.
}

N.B. I'm looking for an elegant (and efficient) solution that does not simply carry out the above and then merely remove all repeat packs (I can already do that but refuse to), but rather get the correct output directly. That's where I'm stuck.

Aucun commentaire:

Enregistrer un commentaire