2017-10-20 8 views
5

J'ai en C++ « moderne », une liste de type:manière la plus élégante de diviser un C++ TypeList

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

Je veux diviser la liste de type selon un prédicat, par exemple std::is_floating_point. Pour être plus précis, mon exemple complet travail est:

#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>(); 
} 

impressions:

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

ma question: vous avez une idée d'un possible solution plus courte/ plus élégante que seulement utilise C++ (pas de problème avec C++ 17) et STL? (Je ne veux pas utiliser une lib auxiliaire comme Boost, Hana ...).

(Ma motivation: Je ne veux pas manquer une ou deux lignes/super solution élégante, comme je vais utiliser cette fonctionnalité largement dans d'autres endroits)


Mon implémentation actuelle est:

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

Juste pour la curiosité, un pointeur historique TypeList (Andrei Alexandrescu 01 Février 2002): http://www.drdobbs.com/generic-programmingtypelists-and-applica/184403813

Répondre

4

quelque chose comme cela peut être un peu plus simple/plus court

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

Très belle solution (à mon humble avis). Mais pourquoi '(Inclure && P :: value) || (! Inclure &&! P :: value) 'et pas simplement' Inclure == P :: value'? – max66

+1

@ max66 oui, c'est plus simple, j'étais juste dans une ambiance booléenne seulement :) édité –

+0

Je sais ... Je suis dans un modèle de méta-programmation seule humeur; mais votre solution est beaucoup plus simple, plus courte et (à mon humble avis) très élégante. – max66

1

Je ne dirai pas que la façon suivante est meilleure ou plus élégante.

C'est différent et c'est mon chemin. Utiliser uniquement la spécialisation des classes de modèles variadiques; pas de fonction.

Devrait également fonctionner avec C++ 11.

Espérons que cet exemple est utile.

#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>>{}, "!"); 
}