async with member function - c++

I've a code:
class cabc{
public:
void pr()
{
cout<<"abcdef";
}
};
int main()
{
cabc cap;
auto f = async(cap.pr);
f.get();
return 0;
}
This code is not working. I know the same thing can be done using:
auto f = async(&cabc::pr,cap);
This is working. But why the first approach is not working?

cap.pr is an incomplete member function call expression. You must follow it with parentheses containing the appropriate function arguments to make a valid C++ expression.
You can't therefore pass cap.pr to std::async or any other function.
To pass a member function to std::async you need to use the syntax you found:
auto f=std::async(&capc::pr,cap);
Though in this case, you need to be aware that the cap object is copied. You could also use
auto f=std::async(&capc::pr,&cap);
to just pass a pointer to cap.
If the pointer-to-member-function syntax is unwelcome then you can use a lambda:
auto f=std::async([&]{cap.pr();});
This isn't quite the same: it doesn't pass the member function pointer and object pointer to std::async, it passes a lambda object containing a reference to cap that calls its pr member function directly. However, the result is essentially the same.

Have a look at the function signature for async:
template< class Function, class... Args>
std::future<typename std::result_of<Function(Args...)>::type>
async( Function&& f, Args&&... args );
(from cppreference)
It should be apparent that, firstly, whatever Function&& is, it can't be the case that cap.pr and &cabc::pr are both of that type.
More precisely, though, Function&& is supposed to be an rvalue-reference to a function pointer. &cabc::pr is just the syntax for a pointer-to-member-function, and because it's a member function, a pointer to the object itself needs to be the first argument. If cap.pr were a C-style function pointer, then your first sample might work.
Honestly, I'm not sure why your second sample works since you're not passing in a pointer to your object.
There are a number of other answers to similar questions. For example: How to, in C++11, use std::async on a member function?

Related

What is the lifetime of a C++ method pointer expression?

I have a generic function that invokes a callable it's handed; the moral equivalent of a call to std::invoke, except it's part of a coroutine:
// Wait for some condition to be true, then invoke f with the supplied
// arguments. The caller must ensure that all references remain valid
// until the returned task is done.
template <typename F, typename Args...>
Task<void> WaitForConditionAndInvoke(F&& f, Args&&... args) {
co_await SomeCondition();
std::invoke(f, std::forward<Args>(args)...);
}
Because of the requirement that the input arguments remain valid, it's not always legal to pass WaitForConditionAndInvoke a temporary like a lambda (unless e.g. the returned task is directly co_awaited as part of a single expression). This is true even if using unary + to convert a lambda with no bound state to a function pointer: the function pointer is itself a temporary, and asan seems to correctly complain about using it after it's destroyed.
My question is whether using a member function pointer is legal:
struct Object {
void SomeMethod();
};
// Some global object; we're not worried about the object's lifetime.
Object object;
Task<void> WaitForConditionAndInvokeOnGlobalObject() {
return WaitForConditionAndInvoke(&Object::SomeMethod, &object);
}
This seems to work fine, but it's unclear to me what the lifetime of the pointer that &Object::SomeMethod evaluates to is. Is this guaranteed to be a constant expression, i.e. not a temporary? What part of the standard covers this?
That WaitForConditionAndInvoke coroutine will be dangerous unless every argument including the functor f refers to an object with lifetime long enough. For example, WaitForConditionAndInvoke(std::abs, 1) has undefined behavior because of the object materialized to initialize a reference with the int prvalue expression 1. There is no difference per the Standard for constant expression arguments here, although a constant expression value could help compilers implement it in a way which "works" using a dead object's known value.
To fix this, you could have your function move every rvalue argument into a local object:
// All rvalue arguments are moved from.
// The caller must make sure all lvalue arguments remain valid until
// the returned task is done.
template <typename F, typename Args...>
Task<void> WaitForConditionAndInvoke(F&& f, Args&&... args) {
// local_f could be declared an lvalue reference or object,
// but not an rvalue reference:
F local_f(std::forward<F>(f));
// Similarly, the template arguments to tuple are lvalue references
// or object types.
std::tuple<Args...> local_args(std::forward<Args>(args)...);
co_await SomeCondition();
std::apply(std::forward<F>(local_f), std::move(local_args));
}
Or to be even safer, do as std::bind does, and move/copy everything. The calling code can specify that the functor or functor argument(s) should be treated as a reference with no move or copy using std::ref or std::cref. In fact, that implementation is just:
// All arguments are moved or copied from.
// A std::reference_wrapper can be used to skip the move/copy of
// the referenced object.
template <typename F, typename Args...>
Task<void> WaitForConditionAndInvoke(F&& f, Args&&... args) {
auto bound_f = std::bind(std::forward<F>(f), std::forward<Args>(args)...);
co_await SomeCondition();
bound_f();
}
As the commenters say, there's no lifetime issue here. &Object::SomeMethod is a constant expression, just like 42 or &WaitForConditionAndInvokeOnGlobalObject.
You might have confused yourself by naming your type struct Object, so the expression &Object::SomeMethod kind of looks like it involves an object... but it doesn't; Object is the name of a type. There is no object involved in that expression; it's a simple compile-time constant, just like offsetof(Object, SomeDataMember) or sizeof(Object) would be.
No object involved = no lifetime issues involved.
EDIT (thus completely changing my answer): Ah, aschepler is right, you're concerned with the lifetime of the thing-referred-to-by-F&& f in
template <typename F, typename Args...>
Task<void> WaitForConditionAndInvoke(F&& f, Args&&... args) {
co_await SomeCondition();
std::invoke(f, std::forward<Args>(args)...);
}
which is the temporary object with value &Object::SomeMethod, which of course dies at the end of its full-expression, i.e., at the semicolon at the end of return WaitForConditionAndInvoke(&Object::SomeMethod, &object);. As soon as you hit that semicolon, all of the temporaries go out of scope and any references to them (like, that f captured in the coroutine frame) are definitely dangling.

What is `R(*pf)(void*, Args...)`, function pointer to a method?

I saw this type here. I believe he's trying to create a variable pf for a member pointer type-erased (that's why there's void* there). I then noticed this type signature in similar such classes.
But according to isocpp a non-static member pointer type is defined like this:
int (Fred::*)(char,float) (for some class Fred)
and a function pointer type is defined like this:
int (*)(char,float)
Therefore one would create a member pointer variable mp like this:
int (S::*mp)(int) = nullptr;
Maybe this void* represents this* and its another way to define a member pointer variable by defining a function pointer variable? Is this possible?
What is R(*pf)(void*, Args...)?
It's the declaration of a function pointer. Nothing more than that.
Compatible functions take void* and Args..., and return R.
In the given example, the compatible function that's assigned to the pointer, is a lambda. The void* is the type-erased address of some callable f, and the Args... members are, well, the arguments that'll be passed to that callable. The callable's type is restored by capturing of type aliases inside the lambda (nice!).
R(*pf)(void*, Args...) is a function pointer (regular one, not pointer-to-member) to a function that returns R and has (void*, Args...) parameters, where Args... is a list of types (an expanded template parameter pack).
Maybe this void* represents this* and its another way to define a member pointer variable
Nah, there is no such feature in C++.
If you look at the code, the only things assigned to this pointer are lambdas, like this one:
pf = [](void* ptr, Args... args)->R{
return blah;
};
I'm not sure why you expected pointers-to-members to be involved.

Address of an overloaded C++ template function involving SFINAE

I need to get the address of an overloaded template function that involves SFINAE. A good example of this scenario would be boost::asio::spawn found here...
https://www.boost.org/doc/libs/1_70_0/doc/html/boost_asio/reference/spawn.html
How would I find the address of this particular instance...
template<
typename Function,
typename Executor>
void spawn(
const Executor & ex,
Function && function,
const boost::coroutines::attributes & attributes = boost::coroutines::attributes(),
typename enable_if< is_executor< Executor >::value >::type* = 0);
I've unsuccessfully tried this...
using Exec = boost::asio::io_context;
using Func = std::function<void(boost::asio::yield_context)>;
void (*addr)(Exec, Func) = boost::asio::spawn;
boost::asio::spawn is not a function. It is a function template. It's a blueprint from which functions can be created. There's no way to get a pointer to a function template because it's a purely compile-time construct.
boost::asio::spawn<Func, Exec> is a function overload set, but it has no overload that matches the signature void(Exec,Func). Remember, default function arguments are just syntactic sugar. Those arguments are still part of the function's signature.
Those two issues make getting a pointer to boost::asio::spawn hard and ugly. It would be much easier to use a lambda. A lambda will let you preserve type deduction and take advantage of the default arguments:
auto func = [](auto&& exec, auto&& func) {
boost::asio::spawn(std::froward<decltype(exec)>(exec),
std::forward<decltype(func)>(func));
};
Even if you absolutely need a function pointer, a lambda is still probably the way to go. You lose parameter type deduction, but can still take advantage of the function's default arguments:
void(*addr)(const Exec&, Func) = [](const Exec& exec, Func func) {
boost::asio::spawn(exec, std::move(func));
};
This works because captureless lambdas can be converted to raw function pointers.
If you really, absolutely need a pointer directly to one of the spawn instantiations for some reason, you can get it, but it's not pretty:
using Exec = boost::asio::io_context::executor_type;
using Func = std::function<void(boost::asio::yield_context)>;
void(*addr)(const Exec&, Func&, const boost::coroutines::attributes&, void*) = boost::asio::spawn<Func&, Exec>;
You lose a lot in doing so though. Not only do you lose argument type deduction and the default arguments, you also lose the ability to pass both lvalues and rvalues to the function since you no longer have a deduced context for forwarding references to work in. I've to get a pointer to the instantiation accepting an lvalue-reference to the function. If you want it to accept rvalue-references instead, use
void(*addr)(const Exec&, Func&&, const boost::coroutines::attributes&, void*) = boost::asio::spawn<Func, Exec>;
Also note that this function takes four parameters. Call it with, i.e.
addr(my_io_context.get_executor(), my_function, boost::coroutines::attributes{}, nullptr);
Example
Tried to put this solution as an edit in the question where it'd be visible right off, but the moderators seem to think it's better to force a reader through to here...
using Exec = boost::asio::io_context;
using Func = std::function<void(boost::asio::yield_context)>;
using Attr = boost::coroutines::attributes;
void (*addr)(const Exec&,
Func&&,
const Attr&,
typename enable_if< is_executor< Executor >::value>::type*
) = boost::asio::spawn<Func, Exec>;
Yes, ugly but not too too bad.

Pointer to member function-object in std::invoke

Why std::invoke does not work with a pointer to member which is a function object with arguments? Like this:
struct MyClass
{
std::function<void(int)> functor{ [](int arg) { printf("%d\n", arg); } };
};
int main()
{
MyClass mc;
std::invoke(&MyClass::functor, mc, 110);
}
Prints : 'std::invoke': no matching overloaded function found.
I checked this in Visual C++ and g++.
Moreover, std::is_invocable_v<decltype(&MyClass::functor), MyClass, int> claims that this functor is not invocable, which is definitely wrong.
Am I missing something or it is a defect in the standard? If this behavior is correct, then what is the point in std::invoke and all its friends? What I mean is that simple function objects can be easily called without any facilities, but I though the main purpose of std::invoke is to generalize and simplify working with all callables, including the tricky ones, like pointers to members. The fact that std::invoke cannot invoke a clearly invocable target seems weird to me.
The problem is that functor is not a function, but a data member. As such, &MyClass::functor is not a pointer to member function, but a pointer to member data. This distinction means that you cannot pass any other arguments to std::invoke, as you cannot call a data member; but for some data members, like your functor you can. The standard is not clear on this before C++11, and so there was LWG issue 1520 which got merged in C++11.
I mean you can rewrite your example as:
std::invoke(&MyClass::functor, mc)(110);
// or for maximum confusion
std::invoke(std::invoke(&MyClass::functor, mc), 110);
but I don't think that's what you want. Thanks to Barry, this is a bad idea, as what would the following code do:
struct X {
std::function<void()> f;
};
std::invoke(&X::f, x); // gets f, or calls f?
Getting f would make it consistent with other data members, but then you have no way of invoking f if f takes no arguments. Calling f means that you get an inconsistency with other data members when you just want to get them.

how to create wrapper around std::bind

I have a function object typedef std::function<bool(Event*)> Handler. A member function always gets assigned to this object. So, I am using std::bind to achieve that.
Handler f = std::bind(&LevelSystem::switchLevel, this, std::placeholders::_1);
f(new Event("test"));
This code above works as expected, but I want to wrap the std::bind in a helper function for cleaner code. This is what I have come up with.
template<class Func> inline Handler MemFn(Func &&f) {
return std::bind(f, this, std::placeholders::_1);
}
And the usage will be:
Handler f = MemFn(&LevelSystem::switchLevel);
I am getting an error when using this function:
No viable conversion from
'__bind<bool(LevelSystem::*&)(Event *), System *,std::__1::placeholders::__ph<1> &>' to
'Handler' (aka 'function<bool(Event *)>')
I do not understand the error.
You're trying to create a bind expression that will call a bool (LevelSystem::*)(Event*) function on a System object, which is not possible.
You need to bind the correct dynamic type of this to the function, as your comment indicates you've now done by passing the this pointer to MemFn.
If you're always going to pass a pointer-to-member-function to MemFn then there's no point passing it by rvalue-reference, you might as well just pass the pointer-to-member. Doing that allows you to deduce the class type, so you can then cast this to that type:
template<typename Ret, typename Class, typename Param>
inline Handler MemFn(Ret (Class::*f)(Param)) {
return std::bind(f, static_cast<Class*>(this), std::placeholders::_1);
}