拆分 C++ TypeList 的最优雅方式
Most elegant way to split a C++ TypeList
在 "modern" C++ 中,我有一个类型列表:
template <typename... T> struct TypeList {};
我想根据谓词拆分类型列表,例如 std::is_floating_point
。更准确地说,我的完整 working 示例是:
#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>]
我的问题:你有没有想过一个可能的 shorter/more 优雅的 解决方案,它只使用 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;
};
出于好奇,指向 TypeList 的历史指针(Andrei Alexandrescu,2002 年 2 月 1 日):http://www.drdobbs.com/generic-programmingtypelists-and-applica/184403813
我并不是说下面的方式更好或更优雅。
这是不同的,这是我的方式。
仅使用可变参数模板的特化类;没有功能。
应该也适用于 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>>{}, "!");
}
这样的东西可能有点simpler/shorter
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<>{} ));
};
在 "modern" C++ 中,我有一个类型列表:
template <typename... T> struct TypeList {};
我想根据谓词拆分类型列表,例如 std::is_floating_point
。更准确地说,我的完整 working 示例是:
#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>]
我的问题:你有没有想过一个可能的 shorter/more 优雅的 解决方案,它只使用 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;
};
出于好奇,指向 TypeList 的历史指针(Andrei Alexandrescu,2002 年 2 月 1 日):http://www.drdobbs.com/generic-programmingtypelists-and-applica/184403813
我并不是说下面的方式更好或更优雅。
这是不同的,这是我的方式。
仅使用可变参数模板的特化类;没有功能。
应该也适用于 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>>{}, "!");
}
这样的东西可能有点simpler/shorter
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<>{} ));
};