Can we use variadic template function to filter parameters of specific type, then pass the rest to another function?

两盒软妹~` 提交于 2021-02-07 05:57:06

问题


for example

// we have a variadic function
void print(...);    

// I need such a function to filter parameters of specific type
template<typename... Args>
void print_filter(const Args&... args)
{
    // filter non-integral type
    print(integral args);
}

// my goal, all non-integral type can be ignored:
print_filter(1.0, 2, "abc", 3) == print(2, 3)

I have used up my knowledge to do that... can you help? or just to prove it's impossible, which also be very helpful. Thanks


回答1:


A neat trick is to convert the arguments you want into a 1-element forwarding tuple, the arguments you don't want into an empty tuple, tuple_cat the results, then apply (C++17) the resulting tuple to the function you want to invoke:

template<typename... Args>
void print_filter(Args&&... args) {
    std::apply(
        [](auto&&... args) { return print(std::forward<decltype(args)>(args)...); },
        std::tuple_cat(
            std::get<std::is_integral<typename std::decay<Args>::type>::value ? 0 : 1>(
                std::make_tuple(
                    [](Args&& arg) { return std::tuple<Args&&>{std::forward<Args>(arg)}; },
                    [](Args&&) { return std::tuple<>{}; }))(
                std::forward<Args>(args))...));
}

Note that this employs another trick, which is to use get to conditionally apply one of two functions to an argument.

Example.




回答2:


#include <cstddef>
#include <type_traits>
#include <utility>
#include <tuple>

template <typename S, typename M, typename O = std::index_sequence<>>
struct filter : O {};

template <std::size_t I, std::size_t... Is, std::size_t... Js, std::size_t... Ks>
struct filter<std::index_sequence<I, Is...>, std::index_sequence<0, Js...>, std::index_sequence<Ks...>>
    : filter<std::index_sequence<Is...>, std::index_sequence<Js...>, std::index_sequence<Ks...>> {};

template <std::size_t I, std::size_t... Is, std::size_t... Js, std::size_t... Ks>
struct filter<std::index_sequence<I, Is...>, std::index_sequence<1, Js...>, std::index_sequence<Ks...>>
    : filter<std::index_sequence<Is...>, std::index_sequence<Js...>, std::index_sequence<Ks..., I>> {};

template <template <typename T> class F, typename... Args>
using Filter = filter<std::make_index_sequence<sizeof...(Args)>, std::index_sequence<F<Args>{}...>>;

template <typename... Args, std::size_t... Is>
void print_filter_impl(std::tuple<Args...>&& tuple, std::index_sequence<Is...>)
{
    print(std::get<Is>(std::move(tuple))...);
}

template <typename... Args>
void print_filter(Args&&... args)
{
    print_filter_impl(std::forward_as_tuple(std::forward<Args>(args)...), Filter<std::is_integral, std::decay_t<Args>...>{});
}

DEMO




回答3:


Here's one way to do it. Start by creating a dummy separator class:

class SEP { };

Then, a helper function that discards all non-integral arguments by pushing the other arguments to the end of the argument list:

template <class T, class... R>
void print_filter_(T x, R... a) {
  if (std::is_integral<T>::value) {
    print_filter_(a..., x);
  } else {
    print_filter_(a...);
  }
}

After going through all the initial arguments, it is left with only the integral ones:

template <class... T>
void print_filter_(SEP, T... a) {
  print(a...);
}

Finally, call the helper function:

template <class... T>
void print_filter(T... a) {
  print_filter_(a..., SEP());
}



回答4:


#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>

template <template <typename> class Predicate, std::size_t N, typename Output, typename... Args> struct find_indices_h;

template <template <typename> class Predicate, std::size_t N, std::size_t... Is, typename First, typename... Rest>
struct find_indices_h<Predicate, N, std::index_sequence<Is...>, First, Rest...> : std::conditional_t<
    Predicate<First>::value,
    find_indices_h<Predicate, N+1, std::index_sequence<Is..., N>, Rest...>,
    find_indices_h<Predicate, N+1, std::index_sequence<Is...>, Rest...>
> {};

template <template <typename> class Predicate, std::size_t N, typename Sequence>
struct find_indices_h<Predicate, N, Sequence> {
    using type = Sequence;
}; 

template <template <typename> class Predicate, typename... Args>
using find_indices = typename find_indices_h<Predicate, 0, std::index_sequence<>, Args...>::type;

template <typename... Args>
void print (Args&&... args) {
    const int a[] = {(std::cout << args << ' ', 0)...};
    static_cast<void>(a);
    std::cout << '\n';
}

template <typename F, typename Tuple, std::size_t... Is>
void partial_apply (F f, Tuple&& tuple, std::index_sequence<Is...>) {
    f(std::get<Is>(std::forward<Tuple>(tuple))...);
}

template<typename... Args>
void print_filter (const Args&... args) {
    const auto partial_print = [](auto&&... ps) { return print(std::forward<decltype(ps)>(ps)...); };
    partial_apply(partial_print, std::forward_as_tuple(args...), find_indices<std::is_integral, Args...>{});
}

int main() {
    std::cout << std::boolalpha;
    print_filter(1, "hello", 'a', true, 1.3, 1000);  // 1 a true 1000
}


来源:https://stackoverflow.com/questions/36747123/can-we-use-variadic-template-function-to-filter-parameters-of-specific-type-the

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!