I wrote a simple C++ template meta function for filtering out types from a list not matching a predicate. This is similar to Filtering variadic template arguments but does pattern matching instead of std::conditional
with the aim of being faster. To that end no empty typelists are concatenated which should incur less instantiations and be faster according to https://blog.galowicz.de/2016/06/25/cpp_template_type_list_performance
The below code works for all my test cases. Any hints how to make it faster (less template instantiations), smaller, better, ...? (Note: Assume appropriate *_t using-templates)
template <typename... Ts>
struct typelist;
template <typename... TLists>
struct concat;
template <typename... Args1, typename... Args2>
struct concat<typelist<Args1...>, typelist<Args2...>>
{
using type = typelist<Args1..., Args2...>;
};
template <class Seq1, class Seq2, class... Seqs>
struct concat<Seq1, Seq2, Seqs...> : concat<Seq1, concat_t<Seq2, Seqs...>>
{
};
template <class TList, template <typename> class Cond>
struct filter;
template<bool matches, template <typename> class Cond, typename...>
struct filter_helper;
template<template <typename> class Cond, typename T, typename... Ts>
struct filter_helper<true, Cond, T, Ts...>
{
using type = concat_t<typelist<T>, filter_t<typelist<Ts...>, Cond>>;
};
template<template <typename> class Cond, typename T, typename... Ts>
struct filter_helper<false, Cond, T, Ts...>
{
using type = filter_t<typelist<Ts...>, Cond>;
};
template<template <typename> class Cond, typename T, typename... Ts>
struct filter<typelist<T, Ts...>, Cond>
{
using type = typename filter_helper<Cond<T>::value, Cond, T, Ts...>::type;
};
template<template <typename> class Cond>
struct filter<typelist<>, Cond>
{
using type = typelist<>;
};
Some tests (TMP_ASSERT
is like static_assert
):
using list1 = tmp::typelist<int, double>;
using list2 = tmp::typelist<float>;
using list3 = tmp::typelist<double>;
using list4 = tmp::typelist<float, double, double>;
using list5 = tmp::typelist<double, float, double>;
using list6 = tmp::typelist<double, double, float>;
using empty = tmp::typelist<>;
template<typename T>
struct is_not_double: std::true_type{};
template<>
struct is_not_double<double>: std::false_type{};
TMP_ASSERT_SAME((tmp::filter_t<list1, is_not_double>), (tmp::typelist<int>));
TMP_ASSERT_SAME((tmp::filter_t<list2, is_not_double>), (tmp::typelist<float>));
TMP_ASSERT_SAME((tmp::filter_t<list3, is_not_double>), (tmp::typelist<>));
TMP_ASSERT_SAME((tmp::filter_t<list4, is_not_double>), (tmp::typelist<float>));
TMP_ASSERT_SAME((tmp::filter_t<list5, is_not_double>), (tmp::typelist<float>));
TMP_ASSERT_SAME((tmp::filter_t<list6, is_not_double>), (tmp::typelist<float>));
TMP_ASSERT_SAME((tmp::filter_t<empty, is_not_double>), (tmp::typelist<>));
Note: Code is from a library licensed under BSD 3-Clause.