c++metaprogrammingvariadic-templatesparameter-pack

Filter the types of a parameter pack


I'd like to know if it's possible to filter the types passed to a variadic template (based on a predicate template) to produce another variadic template containing those types which satisfy the predicate:

/** Filter a parameter pack */    
template <template <class> class,
          template <class...> class,
          class...>
struct filter;
template <template <class> class Pred, template <class...> class Variadic>
struct filter<Pred, Variadic> : Variadic<>
{};
template <template <class> class Pred,
          template <class...> class Variadic,
          class T, class... Ts>
struct filter<Pred, Variadic, T, Ts...>
{
    // FIXME: this just stops at first T where Pred<T> is true
    using type = typename std::conditional<
        Pred<T>::value,
        Variadic<T, Ts...>,    // can't do: Variadic<T, filter<...>>
        filter<Pred, Variadic, Ts...> >::type;
};

As you can see, I haven't found a way to "extract" the parameter pack from the rest of the filtered types.


Solution

  • That should be fairly straight-forward. At the heart you should have something like this:

    template <typename...> struct filter;
    
    template <> struct filter<> { using type = std::tuple<>; };
    
    template <typename Head, typename ...Tail>
    struct filter<Head, Tail...>
    {
        using type = typename std::conditional<Predicate<Head>::value,
                                   typename Cons<Head, typename filter<Tail...>::type>::type,
                                   typename filter<Tail...>::type
                              >::type;
    };
    

    You just need Cons<T, Tuple>, which turns T, std::tuple<Args...> into std::tuple<T, Args...>, and you need to pass the predicate along (left as an exercise). Cons could look like this:

    template <typename, typename> struct Cons;
    
    template <typename  T, typename ...Args>
    struct Cons<T, std::tuple<Args...>>
    {
        using type = std::tuple<T, Args...>;
    };
    

    The result of filter<Args...>::type would be std::tuple<Brgs...>, where Brgs... is a pack consisting of only those types in Args... for which the predicate holds.