获得一包装

时间:2016-03-13 04:53:07

标签: c++ templates c++11 variadic

这是一个非常艰难的(至少对我而言)。我将首先讨论一个我已经解决过的更简单的任务。 ExpandPacks<Packs...>::type是从Packs...中的每个包装中获取一种类型而获得的所有包装的一包。例如

ExpandPacks<P<int, char>, P<bool, double, long>>::type

P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >

我已经为任意数量的包编写了代码来执行此操作:

#include <iostream>
#include <type_traits>

template <typename T, typename Pack> struct Prepend;
template <typename...> struct Merge;

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

template <typename Pack>
struct Merge<Pack> {
    using type = Pack;
};

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

template <typename First, typename... Rest>
struct Merge<First, Rest...> : Merge<First, typename Merge<Rest...>::type> {};

template <typename... Packs> struct ExpandPacks;
template <typename T, typename Pack> struct ExpandPacksHelper;
template <typename T, typename PackOfPacks> struct ExpandPacksHelper2;
template <typename Pack, typename PackOfPacks> struct ExpandPacksHelper3;

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

template <template <typename...> class P, typename T, typename... Packs>
struct ExpandPacksHelper2<T, P<Packs...>> {
    using type = P<typename Prepend<T, Packs>::type...>;
};

template <template <typename...> class P, typename... Ts, typename... Packs>
struct ExpandPacksHelper3<P<Ts...>, P<Packs...>> : Merge<typename ExpandPacksHelper2<Ts, P<Packs...>>::type...> {};

template <template <typename...> class P, typename... Ts, typename Pack>
struct ExpandPacks<P<Ts...>, Pack> : Merge<typename ExpandPacksHelper<Ts, Pack>::type...> {};

template <typename First, typename... Rest>
struct ExpandPacks<First, Rest...> : ExpandPacksHelper3<First, typename ExpandPacks<Rest...>::type> {};

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

int main() {
    std::cout << std::boolalpha << std::is_same<
        typename ExpandPacks<P<int, char>, P<bool, double, long>>::type,
        P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        typename ExpandPacksHelper3<P<short, float>, P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >>::type,
        P< P<short, int, bool>, P<short, int, double>, P<short, int, long>, P<short, char, bool>, P<short, char, double>, P<short, char, long>, P<float, int, bool>, P<float, int, double>, P<float, int, long>, P<float, char, bool>, P<float, char, double>, P<float, char, long> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        typename ExpandPacks<P<short, float>, P<int, char>, P<bool, double, long>>::type,
        P< P<short, int, bool>, P<short, int, double>, P<short, int, long>, P<short, char, bool>, P<short, char, double>, P<short, char, long>, P<float, int, bool>, P<float, int, double>, P<float, int, long>, P<float, char, bool>, P<float, char, double>, P<float, char, long> >
    >::value << '\n';  // true

    std::cout << std::is_same<
        typename ExpandPacks<P<int, bool>, P<short, float>, P<int, char>, P<bool, double, long>>::type,
        P< P<int, short, int, bool>, P<int, short, int, double>, P<int, short, int, long>, P<int, short, char, bool>, P<int, short, char, double>, P<int, short, char, long>, P<int, float, int, bool>, P<int, float, int, double>, P<int, float, int, long>, P<int, float, char, bool>, P<int, float, char, double>, P<int, float, char, long>,
            P<bool, short, int, bool>, P<bool, short, int, double>, P<bool, short, int, long>, P<bool, short, char, bool>, P<bool, short, char, double>, P<bool, short, char, long>, P<bool, float, int, bool>, P<bool, float, int, double>, P<bool, float, int, long>, P<bool, float, char, bool>, P<bool, float, char, double>, P<bool, float, char, long> >
    >::value << '\n';  // true
}

但现在我想概括一下。不必总是从每个包中选择一种类型,而是必须从每个包中选择N种类型,其中N是模板参数。如果N超过某个包的大小,那么只需从该包中取出所有类型。应保留每个包装的类型顺序。但我完全被困在这里。另外,我没有指定输出包的任何特定顺序,这使得测试更加困难。这是一个例子:

ExpandPacks<2, P<int, char, short>, P<bool, double, long>>::type

,最高为包装顺序,

P< P<int, char, bool, double>, P<int, char, bool, long>, P<int, char, double, long>,
P<int, short, bool, double>, P<int, short, bool, long>, P<int, short, double, long>,
P<char, short, bool, double>, P<char, short, bool, long>, P<char, short, double, long> >

通过从P<int, char, short>取2而从P<bool, double, long>取2来获得4包。我也可以定义

ExpandPacks<std::index_sequence<2,1>, P<int, char, short>, P<bool, double, long>>::type

表示从P<int, char, short>获取2,从P<bool, double, long>获取1,一旦第一个问题得到解决(或者立即解决这个问题,这将解决这两个问题),这应该是一个简单的扩展。由于未指定输出包的顺序,我想测试输出的最简单方法是检查

ExpandPacks<1, P<int, char>, P<bool, double, long>>::type

或者,

ExpandPacks<std::index_sequence<1,1>, P<int, char>, P<bool, double, long>>::type

P< P<int, bool>, P<int, double>, P<int, long>, P<char, bool>, P<char, double>, P<char, long> >

因为那必须减少到我已经解决的版本。如果没有人能这么快解决这个问题,我愿意提供赏金。感谢。

更新:出于测试目的,我刚刚编写了一个程序来检查两个类型的类型是否相等,直到类型的排列:http://ideone.com/zb7NA7 这可能对测试有所帮助,因为这里没有指定包的输出具有任何特定顺序。

2 个答案:

答案 0 :(得分:1)

以下适用于两包(可轻松扩展):

#include <type_traits>

template <typename...> struct pack {using type=pack;};
template <typename, typename> struct cat;
template <typename... l, typename... r>
struct cat<pack<l...>, pack<r...>> : pack<l..., r...> {};

template <typename T>
using eval = typename T::type;

//! N choose K

namespace detail{
    template <typename, typename, std::size_t, typename...> struct n_choose_k;

    template <typename head, typename... tail, std::size_t K,  typename... prev>
    struct n_choose_k<std::enable_if_t<(sizeof...(tail)+1 >= K && K > 0)>,
                      pack<head, tail...>, K, prev...> :
      cat<typename n_choose_k<void, pack<tail...>, K-1, prev..., head>::type,
          typename n_choose_k<void, pack<tail...>, K, prev...>::type> {};

    template <typename... tail, typename... prev>
    struct n_choose_k<void, pack<tail...>, 0, prev...> :
        pack<pack<prev...>> {};

    template <typename... tail, std::size_t K, typename... prev>
    struct n_choose_k<std::enable_if_t<(K > sizeof...(tail))>,
                      pack<tail...>, K, prev...> : pack<> {};
}
template <typename p, std::size_t k>
using n_choose_k = eval<detail::n_choose_k<void, p, k>>;

//! Interleave

template <typename P, typename... Packs>
using cat_all = pack<eval<cat<P, Packs>>...>;

template <typename, typename> struct cross_interleave;
template <typename l, typename... ls, typename... r>
struct cross_interleave<pack<l, ls...>, pack<r...>> :
    eval<cat<typename cat_all<l, r...>::type,
                eval<cross_interleave<pack<ls...>, pack<r...>>>>> {};
template <typename... r>
struct cross_interleave<pack<>, pack<r...>> : pack<> {};

template <typename A, std::size_t Na, typename B, std::size_t Nb>
using interleave_multi = eval<cross_interleave<n_choose_k<A, Na>, n_choose_k<B, Nb>>>;

使用interleave_multi<pack<int, char>, 1, pack<bool, double, long>, 2>的示例:

static_assert(
  std::is_same<interleave_multi<pack<int, char>, 1, pack<bool, double, long>, 2>,
               pack<pack<int, bool, double>, pack<int, bool, long int>,
                    pack<int, double, long int>, pack<char, bool, double>,
                    pack<char, bool, long int>, pack<char, double, long int>>>{}, "" );

Demo

答案 1 :(得分:0)

好的,我想我已经获得了我寻求的一般解决方案:

template <typename...> struct Merge;

template <typename Pack>
struct Merge<Pack> { using type = Pack; };

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

template <typename First, typename... Rest>
struct Merge<First, Rest...> : Merge<First, typename Merge<Rest...>::type> {};

// The all-important ExpandPacks class.
template <typename... PackOfPacks> struct ExpandPacks;
template <typename T, typename PackOfPacks> struct ExpandPacksHelper;
template <typename T, typename PackOfPacks> struct ExpandPacksHelper2;

template <template <typename...> class P, typename Pack, typename... Packs>
struct ExpandPacksHelper<Pack, P<Packs...>> {
    using type = P<typename Merge<Pack, Packs>::type...>;
};

template <template <typename...> class P, typename... Packs, typename PackOfPacks>
struct ExpandPacksHelper2<P<Packs...>, PackOfPacks> : Merge<typename ExpandPacksHelper<Packs, PackOfPacks>::type...> {};

template <template <typename...> class P, typename... Packs, typename PackOfPacks>
struct ExpandPacks<P<Packs...>, PackOfPacks> : Merge<typename ExpandPacksHelper<Packs, PackOfPacks>::type...> {};

template <typename First, typename... Rest>
struct ExpandPacks<First, Rest...> : ExpandPacksHelper2<First, typename ExpandPacks<Rest...>::type> {};

// The Expand and ExpandGeneral classes themselves:
template <template <std::size_t, typename> class SubpackType, std::size_t N, typename... Packs>
struct Expand : ExpandPacks<typename SubpackType<N, Packs>::type...> {};  // Expanding using special subpacks of size N within each pack.

template <template <std::size_t, typename> class SubpackType, typename Indices, typename... Packs> struct ExpandGeneral;

template <template <std::size_t, typename> class SubpackType, std::size_t... Is, typename... Packs>
struct ExpandGeneral<SubpackType, std::index_sequence<Is...>, Packs...> : ExpandPacks<typename SubpackType<Is, Packs>::type...> {};  // Just as Expand, but using different N values for each pack.

SubpackType传递到Expand(或ExpandGeneral)将指定扩展中要使用的子包类型,例如Columbo的n_choose_k。但是在这里我用AllAdjacentSubpacks<N, Pack>测试了它,它使用Pack中所有由N个相邻类型组成的子包(Columbo的n_choose_k的一个特例)。例如,我们有测试输出:

std::cout << std::is_same<
    typename ExpandGeneral<AllAdjacentSubpacks, std::index_sequence<2,1,2>, P<short, float, int>, P<int, char>, P<bool, double, long>>::type,
    P< P<short, float, int, bool, double>, P<short, float, int, double, long>, P<short, float, char, bool, double>, P<short, float, char, double, long>, P<float, int, int, bool, double>, P<float, int, int, double, long>, P<float, int, char, bool, double>, P<float, int, char, double, long> >
>::value << '\n';  // true

我在问题开头给出的第一个例子现在只是特例

Expand<AllAdjacentSubpacks, 1, P<int, char>, P<bool, double, long>>::type

给出完全相同的输出。如果您感到好奇,以下是上述测试中使用的AllAdjacentSubpacks<N, Pack>的实现:

// AllAdjacentSubpacks<N, Pack>::type is the pack of all subpacks consisting of N adjacent types in Pack.
template <std::size_t N, typename Pack> struct AllAdjacentSubpacks;
template <std::size_t N, std::size_t Count, typename Pack, typename Output> struct AllAdjacentSubpacksBuilder;
template <std::size_t N, typename Pack> struct Head;
template <std::size_t N, typename Pack, typename Output> struct HeadBuilder;

template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Ts>
struct HeadBuilder<N, P<First, Rest...>, P<Ts...>> : HeadBuilder<N-1, P<Rest...>, P<Ts..., First>> {};

template <template <typename...> class P, typename First, typename... Rest, typename... Ts>
struct HeadBuilder<0, P<First, Rest...>, P<Ts...>> { using type = P<Ts...>; };

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

template <std::size_t N, template <typename...> class P, typename... Ts>
struct Head<N, P<Ts...>> : HeadBuilder<N, P<Ts...>, P<>> {};

template <std::size_t N, std::size_t Count, template <typename...> class P, typename First, typename... Rest, typename... Packs>
struct AllAdjacentSubpacksBuilder<N, Count, P<First, Rest...>, P<Packs...>> : AllAdjacentSubpacksBuilder<N, Count-1, P<Rest...>, P<Packs..., typename Head<N, P<First, Rest...>>::type>> {};

template <std::size_t N, template <typename...> class P, typename First, typename... Rest, typename... Packs>
struct AllAdjacentSubpacksBuilder<N, 0, P<First, Rest...>, P<Packs...>> {
    using type = P<Packs...>;
};

template <std::size_t N, template <typename...> class P, typename... Packs>
struct AllAdjacentSubpacksBuilder<N, 0, P<>, P<Packs...>> {
    using type = P<Packs...>;
};

template <std::size_t N, template <typename...> class P, typename... Ts>
struct AllAdjacentSubpacks<N, P<Ts...>> : AllAdjacentSubpacksBuilder<N, sizeof...(Ts) - N + 1, P<Ts...>, P<>> {};