0

ist Wenn Sie an dem Elementtyp voneine Packung Typen Abflachen, wo nicht-Typ Wert Teil der Abflachung

aussehen
template <typename Pack> struct flatten; 

template <typename T, typename U, std::size_t A, std::size_t B, std::size_t C, typename V, std::size_t D, std::size_t E, typename W> 
struct flatten<std::tuple<T,U, std::index_sequence<A,B,C>, V, std::index_sequence<D,E>, W>> { 
    template <typename, typename, std::size_t, std::size_t, std::size_t, typename, std::size_t, std::size_t, typename> struct S; 
    using type = S<T,U,A,B,C,V,D,E,W>; 
}; 

ist es möglich, dies allgemein zu tun, so dass alle Arten und Nicht-Typen Abflachung in eine einzelne abgeflachte Packung, solange die Nicht-Typen innerhalb eines Typs verpackt sind?

Hier ist meine einfache Lösung für die normale Art der Abflachung, und ich frage mich nur, wie man diese Methode verwendet, um die oben genannten generisch zu arbeiten.

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

template <typename...> struct merge; 

template <typename Pack> 
struct merge<Pack> : identity<Pack> {}; 

template <template <typename...> class P, typename... Ts, typename... Us> 
struct merge<P<Ts...>, P<Us...>> : identity<P<Ts..., Us...>> {}; 

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

template <typename T, template <typename...> class P> struct flatten_h : identity<P<T>> {}; 

template <template <typename...> class P, typename... Ts> 
struct flatten_h<P<Ts...>, P> : merge<typename flatten_h<Ts, P>::type...> {}; 

template <typename Pack> struct flatten; 

template <template <typename...> class P, typename... Ts> 
struct flatten<P<Ts...>> : flatten_h<P<Ts...>, P> {}; 

// Testing 
#include <type_traits> 

template <typename...> struct P; 

int main() { 
    static_assert(std::is_same< 
     flatten<P<int, char, long, P<double, bool>, int>>::type, 
     P<int, char, long, double, bool, int> 
    >::value); 

    static_assert(std::is_same< 
     flatten<P<int, P<bool, bool>, long, P<double, P<char, P<long>>>, int>>::type, 
     P<int, bool, bool, long, double, char, long, int> 
    >::value); 
} 

denke ich C++ 20 auto... (oder ein anderes Schlüsselwort) erlauben sollten beide Typen und nicht-Typ Werte für Vorlagenargumente anzuzeigen.

Antwort

1

Die Aufgabe wird durchführbar, wenn jeder Nicht-Typ-Parameter (entpackt z. B. von std::index_sequence) in seine eigene std::integral_constant verpackt werden kann. Dann gibt es nur Typvorlagenparameter auf der Ebene, auf der die Abflachung auftritt, und man kann einen einfachen Typcontainer wie template<class...> struct Types {}; verwenden.

// upper version for shorter type names; lower version for showing types 
template<auto v> struct Val : std::integral_constant<decltype(v), v> {}; 
//template<auto v> using Val = std::integral_constant<decltype(v), v>; 
// NOTE: bug(?) in GCC 7.2 which maps size_t(0) to false and size_t(1) to true 

template<class I, I... is> 
auto flatten(Type< std::integer_sequence<I, is...> >) { 
    return Flattened<Val<is>...>{}; 
} 

Below und in a live demo Sie ein vollständiges Arbeitsbeispiel mit dieser Beschränkung finden (und der Bug (?) Im Kommentar erwähnt).

Ich wählte, um alle Typen anzugeben, die abgeflacht werden sollten. Alternativ kann man auch auspacken blind "alles in Ordnung" verschiedene Template-Template-Argumente der Form unter Verwendung von template<template<auto, class...> class ToFlatten> usw.

#include <iostream> 
#include <tuple> 
#include <utility> 

// upper version for shorter type names; lower version for showing types 
template<auto v> struct Val : std::integral_constant<decltype(v), v> {}; 
//template<auto v> using Val = std::integral_constant<decltype(v), v>; 
// NOTE: bug(?) in GCC 7.2 which maps size_t(0) to false and size_t(1) to true 

template<size_t... is, class F> 
constexpr decltype(auto) indexer_impl(std::index_sequence<is...>, F f) { 
    return f(Val<is>{}...); 
} 

template<size_t size, class F> 
constexpr decltype(auto) indexer(F f) { 
    return indexer_impl(std::make_index_sequence<size>{}, f); 
} 

//////////////////////////////////////////////////////////////////////////////// 

template<class T_> 
struct Type {}; 

template<class... Ts> 
struct Types { 
    static constexpr auto size = Val<sizeof...(Ts)>{}; 
    using Tuple = std::tuple<Ts...>; 
}; 

template<size_t i, class T> 
using at_t = std::tuple_element_t<i, typename T::Tuple>; 

//////////////////////////////////////////////////////////////////////////////// 

template<class...> struct Flattened; 

template<class I, I... is> using int_seq = std::integer_sequence<I, is...>; 

// specify which types are allowed in a flat type container 
template<class T> struct is_flat : Val<true> {}; 
template<class I, I... is> struct is_flat< int_seq<I, is...> > : Val<false> {}; 
template<class... Ts> struct is_flat< Types<Ts...> > : Val<false> {}; 
template<class... Ts> struct is_flat< Flattened<Ts...> > : Val<false> {}; 

// check if a type is an instantiation of `template<class...> struct Flattened` 
template<class T> struct is_flattened : Val<false> {}; 
template<class... Ts> struct is_flattened<Flattened<Ts...>> : Val<true> {}; 

// specific type container which guarantees to contain `is_flat` types only 
template<class... Ts> struct Flattened : Types<Ts...> { 
    static_assert((... && is_flat<Ts>{})); 
}; 

//////////////////////////////////////////////////////////////////////////////// 

namespace internal { 

auto merge() { 
    return Flattened<>{}; 
} 

template<class... Ts> 
auto merge(Flattened<Ts...> done) { 
    return done; 
} 

template<class... Ts, class... Us> 
auto merge(Flattened<Ts...>, Flattened<Us...>) { 
    return Flattened<Ts..., Us...>{}; 
} 

// merge more than two args: attempt to avoid linear recursion: is it better? 
template<class... Ts, class... Fs> 
auto merge(Flattened<Ts...>, Fs...) { 
    static_assert((... && is_flattened<Fs>{})); 

    using T = Types<Flattened<Ts...>, Fs...>; 

// group the Flattened args into two halves 
    constexpr size_t N = T::size; 
    constexpr size_t N0 = N/2u; 
    constexpr size_t N1 = N-N0; 

    auto h0 = indexer<N0>([] (auto... is) { return merge(at_t<is, T>{}...); }); 
    auto h1 = indexer<N1>([] (auto... is) { return merge(at_t<N0+is, T>{}...); }); 

    return merge(h0, h1); 
} 

template<class T> 
auto flatten(Type<T>) { 
    static_assert(is_flat<T>{}); 
    return Flattened<T>{}; 
} 

template<class I, I... is> 
auto flatten(Type< std::integer_sequence<I, is...> >) { 
    return Flattened<Val<is>...>{}; 
} 

template<class... Ts> 
auto flatten(Type< Types<Ts...> >) { 
    return merge(internal::flatten(Type<Ts>{})...); 
} 

}// internal 

template<class... Ts> 
auto flatten(Types<Ts...>) { 
    return internal::merge(internal::flatten(Type<Ts>{})...); 
} 

//////////////////////////////////////////////////////////////////////////////// 

template<class T> 
void inspect() { 
    std::cout << __PRETTY_FUNCTION__ << std::endl; 
} 

struct Custom {}; 

int main() { 
    auto foo = Types< 
    Types<int, char, long>, 
    Val<7>, 
    Types<double, Val<5>, float, Types<unsigned, Types<Custom, Types<char>, int>>, std::make_index_sequence<4u>>, 
    std::index_sequence<5u,19u,4u>, 
    Types<>, 
    Val<8> 
    >{}; 

    auto bar = flatten(foo); 
    inspect<decltype(bar)>(); 

    return 0; 
} 

Ausgang:

void inspect() [with T = Flattened<int, char, long int, Val<7>, double, Val<5>, float, unsigned int, Custom, char, int, Val<false>, Val<true>, Val<2>, Val<3>, Val<5>, Val<19>, Val<4>, Val<8> >] 

Ausgabe mit mehr Typnamen:

void inspect() [with T = Flattened<int, char, long int, std::integral_constant<int, 7>, double, std::integral_constant<int, 5>, float, unsigned int, Custom, char, int, std::integral_constant<bool, false>, std::integral_constant<bool, true>, std::integral_constant<long unsigned int, 2>, std::integral_constant<long unsigned int, 3>, std::integral_constant<int, 5>, std::integral_constant<long unsigned int, 19>, std::integral_constant<long unsigned int, 4>, std::integral_constant<int, 8> >] 
Verwandte Themen