Create all subpackages of size N from a template package

NSubsets<N, Pack<Types...>>::typemust be a package of packets consisting of all subsets of Types...size N. For example,

NSubsets<2, Pack<int, char, double>>::type

should be

Pack<Pack<int, char>, Pack<int, double>, Pack<char, double>>

One of the methods is simply to take the decision output PowerSetfrom Getting all the subpackages from the package , and then remove every package that does not have size N. But this is too inefficient for large N (and in any case is bad). Here is my idea (inspired by elegant solutions for PowerSet): Suppose we have Pack<A,B,C,D>, and N = 2. Starting with Pack<>, we repeat the types in Pack<A,B,C,D>and add each type as follows: Before adding anything, we have:

Pack<>

Adding A to the previous one (and preserving the previous one as well), we get:

Pack<>, Pack<A>

Adding B to the previous one (and keeping the previous one), we get:

Pack<>, Pack<A>, Pack<B>, Pack<A,B>,

Pack<A,B> 2, , :

Pack<>, Pack<A>, Pack<B>

C ( ), :

Pack<>, Pack<A>, Pack<B>, Pack<C>, Pack<A,C>, Pack<B,C>.

, Pack<A,C>, Pack<B,C>:

Pack<>, Pack<A>, Pack<B>, Pack<C>

D ( ), :

Pack<D>, Pack<A,D>, Pack<B,D>, Pack<C,D>.

2, , ,

Pack<Pack<A,B>, Pack<A,C>, Pack<B,C>, Pack<A,D>, Pack<B,D>, Pack<C,D>>

.

, Pack<> . N 2, .   , , false, , (). , , - , , , .

#include <iostream>
#include <type_traits>

template <int, typename> struct IsSize;

template <int N, template <typename...> class P, typename... Types>
struct IsSize<N, P<Types...>> : std::integral_constant<bool, sizeof...(Types) == N> {};

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

template <int N, template <typename...> class P, typename... KeptPacks, typename... SizeNPacks>
struct PartitionPacksBySizeHelper<N, P<>, P<KeptPacks...>, P<SizeNPacks...>> {
    using not_sizeN_types = P<KeptPacks...>;
    using sizeN_types = P<SizeNPacks...>;
};

template <int N, template <typename...> class P, typename First, typename... Rest, typename... KeptPacks, typename... SizeNPacks>
struct PartitionPacksBySizeHelper<N, P<First, Rest...>, P<KeptPacks...>, P<SizeNPacks...>> : std::conditional<IsSize<N, First>::value,
        PartitionPacksBySizeHelper<N, P<Rest...>, P<KeptPacks...>, P<SizeNPacks..., First>>,
        PartitionPacksBySizeHelper<N, P<Rest...>, P<KeptPacks..., First>, P<SizeNPacks...>>
    >::type {};

template <int, typename> struct PartitionPacksBySize;

template <int N, template <typename...> class P, typename... Packs>
struct PartitionPacksBySize<N, P<Packs...>> : PartitionPacksBySizeHelper<N, P<Packs...>, P<>, P<>> {};

template <typename, typename> struct Append;

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

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

template <int N, template <typename...> class P, typename... CurrentPacks, typename... AccumulatedPacks>
struct NSubsetsHelper<N, P<>, P<CurrentPacks...>, P<AccumulatedPacks...>> {
    using type = P<AccumulatedPacks...>;
};

template <int N, template <typename...> class P, typename First, typename... Rest, typename... KeptPacks, typename... SizeNPacks>
struct NSubsetsHelper<N, P<First, Rest...>, P<KeptPacks...>, P<SizeNPacks...>>
    : NSubsetsHelper<N, P<Rest...>,
        typename PartitionPacksBySize<N, P<KeptPacks..., typename Append<First, KeptPacks>::type...>>::not_sizeN_types,
        typename PartitionPacksBySize<N, P<KeptPacks..., typename Append<First, KeptPacks>::type...>>::sizeN_types> {};

template <int, typename> struct NSubsets;

template <int N, template <typename...> class P, typename...Types>
struct NSubsets<N, P<Types...>> : NSubsetsHelper<N, P<Types...>, P<P<>>, P<>> {};

// -----------------------------------------------------------------------------------------------------------------------------------------------
// Testing

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

int main() {
    std::cout << std::boolalpha << std::is_same< NSubsets<2, Pack<int, char, double>>::type,
        Pack<Pack<int, char>, Pack<int, double>, Pack<char, double>>
    >::value << std::endl;  // false (darn!)
}

, , , false. , , , . ?

:

typename PartitionPacksBySize<N, P<KeptPacks..., typename Append<First, KeptPacks>::type...>>::sizeN_types>

typename Merge<P<SizeNPacks...>, typename PartitionPacksBySize<N, P<KeptPacks..., typename Append<First, KeptPacks>::type...>>::sizeN_types>::type

- , N , N ?

+4
2

PowerPack, , .

1: NAppend<N,Pack<...>,T> T Pack<...>, N.

template<std::size_t,typename,typename>
struct NAppend
{
    using type = void;
};

template<std::size_t N,typename...Ts,typename T>
struct NAppend<N,Pack<Ts...>,T>
{
    using type =
        typename std::conditional<
            sizeof...(Ts)==N,
            void,
            Pack<Ts...,T>
        >::type;
};

2: ShrinkPack void Pack Pack void s.

template<typename,typename U=Pack<>>
struct ShrinkPack
{
    using type = U;
};

template<typename T,typename...Ts,typename...Us>
struct ShrinkPack<Pack<T,Ts...>,Pack<Us...>>
    : std::conditional<
        std::is_void<T>::value,
        ShrinkPack<Pack<Ts...>,Pack<Us...>>,
        ShrinkPack<Pack<Ts...>,Pack<Us...,T>>
      >::type
{
};

3: NPack , .

template<std::size_t,typename,typename U=Pack<>>
struct NPack
{
    using type = U;
};

template<std::size_t N,typename...Ts,typename...Us,typename...Vs>
struct NPack<N,Pack<Pack<Ts...>,Us...>,Pack<Vs...>>
    : std::conditional<
        sizeof...(Ts)==N,
        NPack<N,Pack<Us...>,Pack<Vs...,Pack<Ts...>>>,
        NPack<N,Pack<Us...>,Pack<Vs...>>
      >::type
{
};

: , PowerPack ShrinkPack NPack .

template<std::size_t N,typename,typename T=Pack<Pack<>>>
struct NPowerPack
{
    using type = typename NPack<N,T>::type;
};

template<std::size_t N,typename T,typename...Ts,typename...Us>
struct NPowerPack<N,Pack<T,Ts...>,Pack<Us...>>
    : NPowerPack<N,Pack<Ts...>,typename ShrinkPack<Pack<Us...,typename NAppend<N,Us,T>::type...>>::type>
{
};

:

static_assert(std::is_same<
    NPowerPack<1,Pack<int, char, double>>::type,
    Pack<Pack<int>, Pack<char>, Pack<double>>
>(), "");

static_assert(std::is_same<
    NPowerPack<2,Pack<int, char, double>>::type,
    Pack<Pack<int, char>, Pack<int, double>, Pack<char, double>>
>(), "");

+1

k - , O(n^k) O(2^n). , n , k 1s, Pack.

constexpr:

constexpr int ctz(size_t n) {
    return n & 1 ? 0 : 1 + ctz(n >> 1); 
}

constexpr size_t next_perm_impl(size_t v, size_t t) {
    return (t + 1) | (((~t & -~t) - 1) >> (ctz(v) + 1));
}

constexpr size_t next_perm(size_t v) {
    return next_perm_impl(v, v | (v - 1));
}

Columbo, - :

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

template <size_t size, class result, class>
struct accumulator : result { };

template <size_t j, class... R, class T1, class... T>
struct accumulator<j, Pack<R...>, Pack<T1, T...>>
: accumulator<(j>>1), typename std::conditional<j&1, Pack<R..., T1>, Pack<R...>>::type, Pack<T...>>
{};

j, Pack, . (1 << k) - 1 (1 << N) + (1 << (k-1)) - 1. , , :

template <typename P, typename Result, size_t CUR, size_t LAST>
struct PowerPackImpl;

template <typename P, typename... R, size_t CUR, size_t LAST>
struct PowerPackImpl<P, Pack<R...>, CUR, LAST>
: PowerPackImpl<P,
                Pack<R..., typename accumulator<CUR, Pack<>, P>::type>,
                next_perm(CUR),
                LAST>
{ };

template <typename P, typename... R, size_t LAST>
struct PowerPackImpl<P, Pack<R...>, LAST, LAST>
: Pack<R...> { };

template <typename P, size_t K> struct PowerPack;

template <typename... P, size_t K>
struct PowerPack<Pack<P...>, K>
: PowerPackImpl<Pack<P...>, Pack<>, (1 << K) - 1, (1 << sizeof...(P)) + (1 << (K-1)) - 1>
{ };

:

static_assert(std::is_same<
    typename PowerPack<Pack<int, char, double, float>, 1>::type,
    Pack<Pack<int>, Pack<char>, Pack<double>, Pack<float>>
>::value, "1 works");

static_assert(std::is_same<
    typename PowerPack<Pack<int, char, double, float>, 2>::type,
    Pack<Pack<int, char>, Pack<int, double>, Pack<char, double>, Pack<int, float>, Pack<char, float>, Pack<double, float> >
>::value, "2 works");
+2

All Articles