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

流过昼夜 提交于 2019-12-04 18:48:25

The basic idea is to process the initial list of types into a list of (type, count) pairs, and work from there. First, some primitives:

template<class, size_t> struct counted_type {};
template<class...> struct pack {};

Our representation is going to be a pack of counted_types. To build it, we need to be able to add a type to it:

template<class T, class CT> struct do_push;
template<class T, class...Ts, size_t... Is>
struct do_push<T, pack<counted_type<Ts, Is>...>>{
   using type = std::conditional_t<std::disjunction_v<std::is_same<Ts, T>...>,
        pack<counted_type<Ts, Is + (std::is_same_v<Ts, T>? 1 : 0)>...>,
        pack<counted_type<Ts, Is>..., counted_type<T, 1>>
        >;
};
template<class T, class CT> using push = typename do_push<T, CT>::type;

If the type is already there, we add 1 to the count; otherwise we append a counted_type<T, 1>.

And to use it later, we'll need to be able to remove a type from it:

template<class T, class CT> struct do_pop;
template<class T, class...Ts, std::size_t... Is>
struct do_pop<T, pack<counted_type<Ts, Is>...>>{
   using type = remove<counted_type<T, 0>,
                       pack<counted_type<Ts, Is - (std::is_same_v<Ts, T>? 1 : 0)>...>>;
};

template<class T, class CT> using pop = typename do_pop<T, CT>::type;

remove<T, pack<Ts...>> removes the first instance of T from Ts..., if it exists, and returns the resulting pack (if T doesn't exist, the pack is returned unchanged). The (rather boring) implementation is left as an exercise to the reader.

With push we can easily build a pack of counted_types from a pack of types:

template<class P, class CT = pack<> >
struct count_types_imp { using type = CT; };

template<class CT, class T, class... Ts>
struct count_types_imp<pack<T, Ts...>, CT>
        : count_types_imp<pack<Ts...>, push<T, CT>> {};

template<class P>
using count_types = typename count_types_imp<P>::type;

Now, the actual implementation is

template<class T> struct identity { using type = T; };

template <std::size_t N, typename CT, typename = pack<> > struct PermutationNHelper;

// Workaround for GCC's partial ordering failure
template <std::size_t N, class CT, class> struct PermutationNHelper1;

template <std::size_t N, class... Types, std::size_t... Counts, class... Current >
struct PermutationNHelper1<N, pack<counted_type<Types, Counts>...>, pack<Current...>> {
    // The next item can be anything in Types...
    // We append it to Current... and pop it from the list of types, then
    // recursively generate the remaining items
    // Do this for every type in Types..., and concatenate the result.
    using type = concat<
        typename PermutationNHelper<N-1, pop<Types, pack<counted_type<Types, Counts>...>>,
                                    pack<Current..., Types>>::type...
        >;
};


template <std::size_t N, class... Types, std::size_t... Counts, class... Current >
struct PermutationNHelper<N, pack<counted_type<Types, Counts>...>, pack<Current...>> {
    using type = typename std::conditional_t<
                     N == 0,
                     identity<pack<pack<Current...>>>,
                     PermutationNHelper1<N, pack<counted_type<Types, Counts>...>, 
                                            pack<Current...>>
                  >::type; 
     // Note that we don't attempt to evaluate PermutationNHelper1<...>::type 
     // until we are sure that N > 0
};


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

Normally this can be done in one template with two partial specializations (one for N > 0 and one for N == 0), but GCC's partial ordering is buggy, so I dispatched explicitly with conditional. Actually evaluating the pack expansion in PermutationNHelper1 with N equal to 0 will explode horribly, so the unimaginatively named PermutationNHelper1 is introduced to provide an extra level of indirection and prevent the explosion.

concat is just a variadic version of your Merge (well, typename Merge<...>::type). The implementation is left as an exercise to the reader.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!