2016-03-13 4 views
4

Это очень сложный (для меня как минимум). Я начну с обсуждения более простой задачи, которую я уже решил. 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> > 

Пакеты 4, полученные с 2 из P<int, char, short> и 2 из P<bool, double, long>. Я мог бы альтернативно определить

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

означает взять 2 из P<int, char, short> и 1 из P<bool, double, long>, что должно быть легко расширение после того, как первая проблема решается (или решить этот сразу, который будет решать оба вопроса). Поскольку порядок выводимых пачек не указан, я думаю, самый простой способ проверить вывод, чтобы проверить, что

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> > 

так это должно свести к версии, которую я уже решил. Я буду предлагать щедрость, если никто не сможет так быстро решить эту проблему. Благодарю.

Update: Для целей тестирования, я просто написал программу, чтобы проверить, если два пакета типов равны друг другу с точностью до перестановки типов: http://ideone.com/zb7NA7 Это может быть помощь в тестах с выходом пакеты не указаны в каком-либо конкретном порядке здесь.

+0

Вы можете добавить пример? –

+0

@Piotr Skotnicki Пример добавлен в вопрос. – prestokeys

ответ

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.

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. 

Passing SubpackType в Expand (или ExpandGeneral) будет указывать, какой тип subpacks должны быть использованы в разложении, например Columbo-х n_choose_k. Но здесь я протестировал его с помощью AllAdjacentSubpacks<N, Pack>, который использует все подпакеты, состоящие из N смежных типов, в Pack (частный случай Columbus's 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<>> {}; 
Смежные вопросы