Reputation: 3640
I was going to ask a question, but have found an answer on my own while writing it. The question is how to filter a tuple given a template template predicate without specializing it.
Example usage:
using tuple_list_t = std::tuple<std::string, int, none, double, char, abc, bool>;
using tuple_found_expected_t = std::tuple<int, double, char, bool>;
// filtering a tuple
using tuple_filtered_t = filter_t<tuple_list_t, std::is_fundamental>;
static_assert(std::is_same<tuple_filtered_t, tuple_found_expected_t>(), "");
If someone have any advices or remarks about the problem, please, write an answer or a comment.
Upvotes: 0
Views: 313
Reputation: 66200
Sorry but your solution seems to me over-complicated. Particularly regarding the SFINAE part (why the void
?).
What about simply as follows?
template <typename, template <typename...> class>
struct filter;
template <typename ... Ts,
template <typename...> class Pred>
struct filter<std::tuple<Ts...>, Pred>
{
using type = decltype(std::tuple_cat(std::declval<
std::conditional_t<Pred<Ts>::value,
std::tuple<Ts>,
std::tuple<>>>()...));
};
template <typename Tpl,
template <typename...> class Pred>
using filter_t = typename filter<Tpl, Pred>::type;
Upvotes: 1
Reputation: 3640
Here is a version of a template SFINAE struct to filter an std::tuple
of types given a SFINAE predicate. So the invocation looks just like using an STL algorithm with an unary predicate.
Example:
using tuple_list_t = std::tuple<std::string, int, none, double, char, abc, bool>;
using tuple_found_expected_t = std::tuple<int, double, char, bool>;
// filtering a tuple
using tuple_filtered_t = filter_t<tuple_list_t, std::is_fundamental>;
static_assert(std::is_same<tuple_filtered_t, tuple_found_expected_t>(), "");
There were two major problems that I have faced.
eld::filter<tuple, std::is_fundamental>
. Here std::is_fundamental
is a template itself and expects to be specialized. But I can't specialize it before the invocation - I need to specialize it later with the types from a tuple.void
(for example) default specializtion:template <typename Tuple, template <typename> class SFINAEPredicate, typename = void>
SFINAEPredicate<T>::value
.file too big
and up to this point I have found no working way to circumvent this problem (optimization pragmas don't do nothing). But that is out of scope of this question/answer.using type = decltype(std::tuple_cat(append_if_t<std::tuple<>, Types, SFINAEPredicate>()...));
append_if_t
returns a tuple, which will be empty in some cases, and then all of them are concatenated in a final tuple with an std::tuple_cat
.append_if_t
which can be reused by the compiler and no recursive intermediate specializations.Here is the code:
template<typename Tuple, typename T, template<typename> class /*SFINAEPredicate*/, typename = T>
struct append_if;
template<typename T, template<typename> class SFINAEPredicate, typename ... Types>
struct append_if<std::tuple<Types...>, T, SFINAEPredicate, T>
{
using type = typename std::conditional<SFINAEPredicate<T>::value,
std::tuple<Types..., T>, std::tuple<Types...>>::type;
};
template<typename Tuple, typename T, template<typename> class SFINAEPredicate, typename = T>
using append_if_t = typename append_if<Tuple, T, SFINAEPredicate>::type;
// TODO: function to traverse a tuple and find a type (index) that can be used to initialize a POD element
// pass a template parameter (trait validator) that accepts a type
// template for find_if in tuple
template<typename Tuple, template<typename> class /*SFINAEPredicate*/, typename = void>
struct filter;
template<template<typename> class SFINAEPredicate, typename ... Types>
struct filter<std::tuple<Types...>, SFINAEPredicate, void>
{
using type = decltype(std::tuple_cat(append_if_t<std::tuple<>, Types, SFINAEPredicate>()...));
};
template<typename Tuple, template<typename> class SFINAEPredicate, typename = void>
using filter_t = typename filter<Tuple, SFINAEPredicate>::type;
Upvotes: 0