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)...);