2017-10-20 31 views
5

W „nowoczesnego” C++, mam listę typ:najbardziej elegancki sposób, aby podzielić C++ typelist

template <typename... T> struct TypeList {}; 

Chcę podzielić listę typów według orzecznika, na przykład std::is_floating_point. Aby być bardziej precyzyjnym, mój pełny pracy przykładem jest:

#include <iostream> 
#include <type_traits> 

template <typename... T> struct TypeList {}; 

// SplitTypeList<> implementation defined at the end of this post... 

template <typename T> 
void printType() 
{ 
    std::cout << "\n" << __PRETTY_FUNCTION__; 
} 

int main() 
{ 
    struct A 
    { 
    }; 

    using typeList = TypeList<int, double, float, A, int>; 

    using splited_typeList = SplitTypeList<std::is_floating_point, typeList>; 

    using float_typeList = splited_typeList::predicate_is_true_typeList_type; 
    using other_typeList = splited_typeList::predicate_is_false_typeList_type; 

    printType<float_typeList>(); 
    printType<other_typeList>(); 
} 

drukuje:

g++ -std=c++17 typeList.cpp -o typeList; ./typeList 

void printType() [with T = TypeList<double, float>] 
void printType() [with T = TypeList<int, main()::A, int>] 

moje pytanie: mieć wyobrażenie o możliwej krótszym/bardziej eleganckie rozwiązanie, że tylko używa C++ (nie ma problemu z C++ 17) i STL? (Nie chcę używać pomocniczej biblioteki, jak Boost, Hana ...).

(Moja motywacja: Nie chcę stracić jeden lub dwa wiersze/Super eleganckie rozwiązanie, jak będę korzystać z tej funkcji w znacznym stopniu w innych miejscach)


Moja obecna implementacja jest:

namespace Details 
{ 
    template <template <typename> class PREDICATE, 
      typename... TYPELIST_PREDICATE_IS_TRUE, 
      typename... TYPELIST_PREDICATE_IS_FALSE> 
    constexpr auto splitTypeList(TypeList<TYPELIST_PREDICATE_IS_TRUE...>, 
           TypeList<TYPELIST_PREDICATE_IS_FALSE...>, 
           TypeList<>) 
    { 
    return std::make_pair(TypeList<TYPELIST_PREDICATE_IS_TRUE...>(), 
          TypeList<TYPELIST_PREDICATE_IS_FALSE...>()); 
    } 

    template <template <typename> class PREDICATE, 
      typename... TYPELIST_PREDICATE_IS_TRUE, 
      typename... TYPELIST_PREDICATE_IS_FALSE, 
      typename T, 
      typename... TAIL> 
    constexpr auto splitTypeList(TypeList<TYPELIST_PREDICATE_IS_TRUE...>, 
           TypeList<TYPELIST_PREDICATE_IS_FALSE...>, 
           TypeList<T, TAIL...>) 
    { 
    if constexpr (PREDICATE<T>::value) 
    { 
     return splitTypeList<PREDICATE>(
      TypeList<TYPELIST_PREDICATE_IS_TRUE..., T>(), 
      TypeList<TYPELIST_PREDICATE_IS_FALSE...>(), 
      TypeList<TAIL...>()); 
    } 
    else 
    { 
     return splitTypeList<PREDICATE>(
      TypeList<TYPELIST_PREDICATE_IS_TRUE...>(), 
      TypeList<TYPELIST_PREDICATE_IS_FALSE..., T>(), 
      TypeList<TAIL...>()); 
    } 
    } 

    template <template <typename> class PREDICATE, typename... T> 
    constexpr auto splitTypeList(TypeList<T...>) 
    { 
    return splitTypeList<PREDICATE>(
     TypeList<>(), TypeList<>(), TypeList<T...>()); 
    } 
} 

template <template <typename> class PREDICATE, typename TYPELIST> 
struct SplitTypeList; 

template <template <typename> class PREDICATE, typename... TAIL> 
struct SplitTypeList<PREDICATE, TypeList<TAIL...>> 
{ 
    using pair_type = decltype(
     Details::splitTypeList<PREDICATE>(std::declval<TypeList<TAIL...>>())); 
    using predicate_is_true_typeList_type = typename pair_type::first_type; 
    using predicate_is_false_typeList_type = typename pair_type::second_type; 
}; 

Tylko dla ciekawości, historyczny wskaźnik typelist (Andrei Alexandrescu, 01 lutego 2002): http://www.drdobbs.com/generic-programmingtypelists-and-applica/184403813

Odpowiedz

4

coś w tym może być nieco prostsze/krótsza

template< bool, template<typename> class, class... Vs > 
auto FilterImpl(TypeList<>, TypeList<Vs...> v) { return v; } 

template< bool Include, template<typename> class P, class T, class... Ts, class... Vs > 
auto FilterImpl(TypeList<T,Ts...>, TypeList<Vs...>) { return FilterImpl<Include,P>(
    TypeList<Ts...>{} , 
    std::conditional_t< Include == P<T>::value, TypeList<T,Vs...>, TypeList<Vs...> >{} 
); } 

template <template <typename> class PREDICATE, typename TYPELIST> 
struct SplitTypeList 
{ 
    using predicate_is_true_typeList_type = decltype(FilterImpl<true,PREDICATE>(TYPELIST{}, TypeList<>{})); 
    using predicate_is_false_typeList_type = decltype(FilterImpl<false,PREDICATE>(TYPELIST{}, TypeList<>{})); 
}; 
+2

Bardzo ładne rozwiązanie (IMHO). Ale dlaczego '(Include && P :: value) || (! Include &&! P :: value) 'a nie po prostu' Include == P :: value'? – max66

+1

@ max66 yep, to prostsze, byłem po prostu w boolowskim nastroju :) edytowane –

+0

Wiem ... Jestem w szablonowym meta-programowaniu tylko nastrój; ale twoje rozwiązanie jest o wiele prostsze, krótsze i (IMHO) bardzo eleganckie. – max66

1

Mówię, że następująca droga jest lepsza lub bardziej elegancka.

Jest inaczej i to jest moja droga.

Używanie wyłącznie specjalizacji klas szablonów variadycznych; brak funkcji.

Powinien również działać z C++ 11.

Mam nadzieję, że ten przykład pomoże.

#include <tuple> 
#include <type_traits> 

template <template <typename> class Pred, typename> 
struct PredValFirst : public std::false_type 
{ }; 

template <template <typename> class Pred, 
      template <typename...> class C, 
      typename T0, typename ... Ts> 
struct PredValFirst<Pred, C<T0, Ts...>> : public Pred<T0> 
{ }; 


template <template <typename> class Pred, typename List, 
      typename = std::tuple<>, typename = std::tuple<>, 
      bool = PredValFirst<Pred, List>::value> 
struct SplitTypeList; 

template <template <typename> class Pred, template <typename...> class C, 
      typename T0, typename ... Ts, typename ... Tt, typename Lf> 
struct SplitTypeList<Pred, C<T0, Ts...>, std::tuple<Tt...>, Lf, true> 
    : SplitTypeList<Pred, C<Ts...>, std::tuple<Tt..., T0>, Lf> 
{ }; 

template <template <typename> class Pred, template <typename...> class C, 
      typename T0, typename ... Ts, typename Lt, typename ... Tf> 
struct SplitTypeList<Pred, C<T0, Ts...>, Lt, std::tuple<Tf...>, false> 
    : SplitTypeList<Pred, C<Ts...>, Lt, std::tuple<Tf..., T0>> 
{ }; 

template <template <typename> class Pred, template <typename...> class C, 
      typename ... Tt, typename ... Tf> 
struct SplitTypeList<Pred, C<>, std::tuple<Tt...>, std::tuple<Tf...>, false> 
{ 
    using types_true = C<Tt...>; 
    using types_false = C<Tf...>; 
}; 

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

struct A 
{ }; 

int main() 
{ 
    using typeList = TypeList<int, double, float, A, int>; 

    using splited_typeList = SplitTypeList<std::is_floating_point, typeList>; 

    using float_typeList = splited_typeList::types_true; 
    using other_typeList = splited_typeList::types_false; 

    static_assert(std::is_same<float_typeList, 
           TypeList<double, float>>{}, "!"); 
    static_assert(std::is_same<other_typeList, 
           TypeList<int, A, int>>{}, "!"); 
} 
Powiązane problemy