2017-10-20 84 views
5

在「現代」 C++最優雅的方式,我有一個類型列表:拆分C++類型串

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

我想根據謂詞拆分類型列表,例如std::is_floating_point。更精確地說,我完全工作例子是:

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

打印:

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

我的問題:你有可能較短/更優雅解決方案的想法,只使用C++(C++ 17沒有問題)和STL? (我不想使用像Boost,Hana這樣的輔助庫)。

我的動機:我不想錯過一行或兩行/超級優雅的解決方案,我將在其他地方廣泛使用此功能)


我目前的實現是:

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

只是出於好奇,歷史的指針類型串(安德烈Alexandrescu的,2002年02月01日):http://www.drdobbs.com/generic-programmingtypelists-and-applica/184403813

回答

4

這樣的事情可能會有些簡單/短

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

非常好的解決方案(恕我直言)但是爲什麼'(包括&& P :: value)|| (!Include &&!P :: value)'而不是簡單地'Include == P :: value'? – max66

+1

@ max66是的,這很簡單,我只是在一個布爾唯一的心情:)編輯 –

+0

我知道...我在模板元編程唯一的心情;但是你的解決方案更簡單,更短,並且(恕我直言)非常優雅。 – max66

1

我不認爲以下方法更好或更優雅。

這是不同的,這是我的方式。

僅使用可變參數模板類的專門化;沒有功能。

還應該與C++ 11一起工作。

希望這個例子有所幫助。

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