Call function with part of variadic arguments

谁说我不能喝 提交于 2019-12-02 00:42:50

First, we need a function to retrieve the number or arguments the function requires. This is done using function_traits:

template <class F>
constexpr std::size_t nb_args() {
   return utils::function_traits<F>::arity;
}

And with the help of std::index_sequence, we only dispatch the nb_args<F>() first arguments:

template<typename F, std::size_t... Is, class Tup>
void foo_impl(F && f, std::index_sequence<Is...>, Tup && tup) {
    std::forward<F>(f)( std::get<Is>(tup)... );
}

template<typename F, typename... Args>
void foo(F && f, Args&&... args) {
    foo_impl(std::forward<F>(f),
             std::make_index_sequence<nb_args<F>()>{},
             std::forward_as_tuple(args...) );
}

Demo

First, use the following code that lets you find the arity of a lambda or function reference:

template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
{
    using result_type = ReturnType;
    using arg_tuple = std::tuple<Args...>;
    static constexpr auto arity = sizeof...(Args);
};

template <typename R, typename ... Args>
struct function_traits<R(&)(Args...)>
{
    using result_type = R;
    using arg_tuple = std::tuple<Args...>;
    static constexpr auto arity = sizeof...(Args);
};

Next, you forward the variadic arguments along using a tuple pack, and you only expand out to the arity of the function:

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

template<typename F, typename... Args>
void foo(F && f, Args&&... args) {
    foo_impl(std::forward<F>(f),
             std::make_index_sequence<function_traits<F>::arity>{},
             std::forward_as_tuple(args...) );
}

Live example: http://coliru.stacked-crooked.com/a/3ca5df7b55c427b8.

Trivial and hardly extensible solution would be to create a wrapper, that will be called with all arguments, but will use only first few of them.

template<typename F, typename... Args>
void foo(F function, Args... args)
{
    // with proper forwarding if needed
    auto lambda = [](auto fnc, auto first, auto second, auto...)
    {
        fnc(first, second);
    };
    lambda(function, args...);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!