Reputation: 243
Thing simple enough, I want to forward the call of a member function, along with its arguments, as described in the following snippet.
Please note that this is not a duplicate of this question, nor this one.
#include <iostream>
#include <functional>
template<class Function, class ... Args>
auto forward_args(Function&& f, Args&& ... args)
{
return f(std::forward<Args>(args)...);
}
int f(int i) { return i; }
struct A {
int get(int i) const { return i; }
};
int main()
{
std::cout << forward_args(f, 2) << std::endl; //ok
A a;
//std::cout << forward_args(&A::get, a, 2) << std::endl; //ko
static auto wrong_wrapper = &A::get;
//std::cout << forward_args(wrong_wrapper, a, 2) << std::endl; //ko again
static std::function<int (const A&, int)> wrapper = &A::get;
std::cout << forward_args(wrapper, a, 2) << std::endl;
}
The commented lines in the main
function don't compile (g++ 10.2.0 -- error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘f (...)’, e.g. ‘(... ->* f) (...)’
)
I don't quite understand what the compiler is trying to tell me, considering the last cll with the std::function
wrapper does work. And, beside fixing the code, I'd also like to know why it doesn't work.
Upvotes: 1
Views: 1147
Reputation: 243
Alright, first thing, I still don't understand why forward_args(&A::get, a, 2)
doesn't work. Part of the answer was "you need this
", but I actually provide it with the second parameter, right ? How is that different from the std::function
wrapper ?
On the other hand, while the workarounds proposed in above answer work on the snippet, I actually simplified my original problem too much. I actually need to launch tasks asynchronously, in the following code
tasks
, which is wy I start building up wrappers#include <iostream>
#include <future>
#include <functional>
#include <queue>
std::queue<std::function<void()>> tasks;
template<class Function, class ... Args>
auto enqueue(Function&& f, Args&& ... args) -> std::future<decltype(f(args...))>
{
std::function<decltype(f(args...))()> func = std::bind(std::forward<Function>(f), std::forward<Args>(args)...);
auto task_ptr = std::make_shared<std::packaged_task<decltype(f(args...))()>>(func);
std::function<void()> wrapper = [task_ptr]() //wrapper to match types of 'tasks'... ugly
{
(*task_ptr)();
};
tasks.push(wrapper);
return task_ptr->get_future();
}
void indep() {}
struct A {
int get(int i) const { return i; }
};
int main()
{
enqueue(indep);
A a;
//enqueue(&A::get, a, 2); //wrong
static auto wrapper_wrong = &A::get;
//enqueue(wrapper_wrong, a, 2); //wrong again
static std::function<int(const A&,int)> wrapper = &A::get;
enqueue(wrapper, a, 2); //ok
static auto autoptr = std::mem_fn(&A::get);
enqueue(autoptr, a, 2); //ok again
}
Upvotes: 0
Reputation: 180
Calling a member function through pointer-to-member still requires this
pointer, as in usual (direct) invocations. Simply put, you could succeeded calling A::get()
like
static auto wrong_wrapper = &A::get;
(a.*wrong_wrapper)(2);
but what you got after forward_args
was instantiated is
A::get(a, 2);
which is not the correct syntax in its nature.
Solution
As it has been already said in the comments section, if you are allowed to use C++17, employ std::invoke
. If you aren't, you can work it around using std::reference_wrapper
, which accepts any callable type.
template<class Function, class ... Args>
auto forward_args(Function f, Args&& ... args)
{
return std::ref(f)(std::forward<Args>(args)...);
}
I don't forward f
here because std::reference_wrapper
requires that the object passed is not an rval.
UPD:
Don't forget to specify the trailing return type of forward_args
if you use C++11
template<class Function, class ... Args>
auto forward_args(Function f, Args&& ... args) -> decltype(std::ref(f)(std::forward<Args>(args)...))
{
return std::ref(f)(std::forward<Args>(args)...);
}
Upvotes: 5
Reputation: 20938
std::function
works because it uses std::invoke which handles calling pointer to member function.
As the solution you could write:
template<class Function, class ... Args>
auto forward_args(Function&& f, Args&& ... args) {
return std::invoke(std::forward<Function>(f), std::forward<Args>(args)...);
}
Syntax for calling member function for an object are:
obj.memberFunction();
obj->memberFunction();
or if you have a pointer to member function:
using Ptr = int (A::*)(int) const;
Ptr p = &A::get;
A a;
(a.*p)(1); // [1]
(obj.*memberFuncPtr)(args...);
the line [1] is valid syntax for calling member function pointed by a pointer. In your case you try A::get(a,2)
which is just not valid and cannot work.
Upvotes: 1