Home > Mobile >  how to call a function through a variable number of parameters
how to call a function through a variable number of parameters

Time:08-31

How to make a function call with a variable number of parameters? to make it look something like this:

 if (f(args...))

Example to reproduce:

template <class callable, class... arguments>
void timer(callable&& f, arguments&&... args )
{
    f(args...);
}

class Client
{
    public:
    void receive(int, int)
    {
    }
    void sub(int x, int y)
    {
        timer(&Client::receive, this, x, y);
    }
};

int main()
{
    Client cl;
    cl.sub(1,2);
}
main.cpp:4:6: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘f (...)’, e.g. ‘(... ->* f) (...)’
    4 |     f(args...);
      |     ~^~~~~~~~~

CodePudding user response:

You can use std::invoke(f, args...); instead of f(args...)


I've rarely used pointers to member functions, to be honest, but on cppreference I see that for a pointer to a unary member function f of a class C, being c such an object of class C, the call syntax would be (c.*p)(x), being x the argument other than this/c.

This means that if you don't want to use std::invoke, you'd have to extract the first element of args... and the rest of them and pass them like this: (first_of_args.*f)(rest_of_args...). While retrieving first_of_args is relatively easy (std::get<0>(std::forward_as_tuple(args...));), obtaining the pack rest_of_args requires some meta-programming trick, so I guess std::invoke is just the best solution. (The problem is exacerbated by the fact that you can't pass some_obj_ptr->*some_pointer_to_member_fun around, but you must apply it immediately. If that was not the case, you could think of constructing a std::tuple with all but the first element from std::forward_as_tuple(args...) and then use std::apply to pass them all to first_of_args->*f.)

Actually, an idea just came to my mind: using a generic variadic lambda to destructure args... in first and rest... in order to be able to meet the member function pointer call syntax. In code, you'd change your non-working f(args...) with the following

    [&f](auto first, auto... rest){
        return (first->*f)(rest...);
    }(args...);

But this is, directly or indirectly, what std::invoke would do for you.


¹ As highlighted in a comment, you'd rather forward those args perfectly:

std::invoke(std::forward<callable>(f), std::forward<arguments>(args)...);
  • Related