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.
Related
I defined a class that receives an lambda function through constructor. The code is as follows. Why did the definition of t0 pass compilation after using the std::forward, and t1 incur an error?
#include <iostream>
template <typename Func>
class Test {
public:
Test(Func &&func) : m_func(std::forward<Func &&>(func)) {}
void Run() { m_func(); }
private:
Func &&m_func;
};
template <typename Func>
class Foo {
public:
Foo(Func &func) : m_func(func) {}
void Run() { m_func(); }
private:
Func &m_func;
};
int main() {
const auto print = []() { std::cout << "Hello" << std::endl; };
using Print = decltype(print);
Test<decltype(print)> t0(std::forward<Print&&>(print));
t0.Run();
Test<void()> t1(Print{});
t1.Run();
Foo<decltype(print)> t3(std::forward<Print&&>(print));
t3.Run();
Foo<void()> t4(Print{});
t4.Run();
}
[Update]
The definition of t1 should be as following. thx for #JaMiT.
Test<void(*)()> t1([]() { std::cout << "Hello" << std::endl; });
But I'm still confused about the definition of t0. If I deletes the std::forward, it incurs a compilation error.
[Update]
It works if I change the definition of t0 to Test<void (*)()> t0(print);. What's the difference between Test<decltype(print)> t0(print); that causes a compilation error?
Why did the definition of t0 pass compilation after using the std::forward,
Because that is how you declared the constructor of Test. The constructor takes as its parameter an rvalue reference to the template parameter. You explicitly provided decltype(print) as the template argument, so the constructor takes an rvalue of that type. There will be no copying (no pass by value), and an lvalue reference will not cut it. You must provide an rvalue.
By adding std::forward<Print&&>, you converted print to an rvalue. (It would have been simpler to add std::move instead. Rule of thumb: use std::forward when dealing with a "placeholder" type, such as a template parameter, and use std::move when dealing with a fixed type.)
Caution: After using std::forward<Print&&>(print) or std::move(print), you should treat print as uninitialized. That is, your initialization of t3 is a potential bug.
Another tweak that would make this compile is to specify decltype(print)& (with the ampersand at the end) as the template argument. When Func is an lvalue reference, Func&& collapses to Func, which means the constructor would take an lvalue reference instead of an rvalue reference. (Reference collapsing is a key component of forwarding references, on which perhaps you based your code? However, forwarding references would require the constructor to itself be a template.)
and t1 incur an error?
For t1, you specified the template argument as void(), which is the type of a function. Lambdas are objects, not functions, so there is a type mismatch.
On the other hand, a lambda with no captures (nothing inside the []) can implicitly convert to a pointer to a function. This is a place where confusion lurks, because functions also decay to pointers so people can get used to interchanging function types and pointer to function types. To specify a pointer to a function, use void(*)() instead of void().
Caution: Implicit conversions can wreak havoc when combined with references. Then again, you were already in the danger zone when you combined temporary objects (Print{}) with references. Your code would be safer if you changed the data member to Func m_func;. In addition to avoiding dangling references, this would be more efficient (less indirection) when storing a pointer-to-function.
template <typename Func>
class Test {
public:
// Constructor can still take a reference and forward it to the member.
Test(Func &&func) : m_func(std::forward<Func &&>(func)) {}
void Run() { m_func(); }
private:
Func m_func; // Not necessarily a reference
};
There are still potential issues (e.g. Func could be specified as a reference type), but at least this is safer. I choose to treat the remaining issues as out-of-scope for this question about syntax.
It works if I change the definition of t0 to Test<void (*)()> t0(print);.
This combines some concepts I presented earlier. The template argument is now a pointer to a function, so your lambda (print) will undergo an implicit conversion, similar to the t1 case. The result of an implicit conversion is an rvalue, which is what your constructor expects (no need to forward or move).
Caution: By "works", you really mean "compiles". The fact that you asked this question suggests you already know the following, but for the benefit of others: getting code to compile is a necessary step, but that by itself does not mean the code is correct and works as intended. Don't be satisfied when a tweak you do not understand makes your code compile – ask questions!
myNameSpace::functionName(this, {"abc", "abc1"}); // working fine
working fine, but
std::thread(myNameSpace::functionName<ClassName>(this, {"abc", "abc1"}));
//error: invalid use of void expression.
Please suggest me what I am missing here. or how to do it.
namespace myNameSpace {
template<typename T>
void functionName(T* cm, std::initializer_list<std::string_view> args) { ... }
}
First, that is not how you start a thread. You are calling it, and passing the result as an argument to start the thread. But, void is not a valid argument for starting a thread, nor is it indeed any sort of useful expression in any sense.
You'd be writing something like this instead:
std::thread(&myNameSpace::functionName<ClassName>, this, {"abc", "abc1"});
Your next problem is that there is no facility in the language for the compiler to deduce what you mean by {"abc", "abc1"} there. With a direct function call to functionName, it can work it out, because the function call machinery knows what the candidate overloads are, but that is just not the case with this "broken down" construction.
It's possible to fix that by specifying the type:
std::thread(&myNameSpace::functionName<ClassName>, this, std::initializer_list<std::string_view>{"abc", "abc1"});
However, I recommend you just pass (and accept) a std::vector<std::string_view> instead. It'll be much easier and much simpler.
std::thread receives a callable as first parameter, you sent it void (The invoke result of myNameSpace::functionName).
What you need to do is
std::thread(myNameSpace::functionName<ClassName>, this, std::initializer_list<std::string_view>({"abc", "abc1"})));
Note that explicit casting to std::initializer_list is required, as std::initializer_list is non deductible type.
For threads you need to provide function reference and arguments parameters in arguments list like following:
template< class Function, class... Args >
explicit thread( Function&& f, Args&&... args );
in your case you have to provide the arguments as following:
std::thread(myNameSpace::functionName<ClassName>,this, std::initializer_list<std::string_view>({"abc", "abc1"}));
here is function to register.
template <typename ReqT, typename RespT>
bool RegisterCmdHandler(int cmd_id, std::function<int(int, const ReqT&, RespT&)> sync_handler) {
// ... do something with sync_handler and register it for later callback
return true;
}
and a specific handler to register:
int SomeHandler(int id, const Foo& req, Bar& resp) {
// ... specific logic
}
now I want to apply the Handler to Register Function, compiler complains
RegisterCmdHandler(1, SomeHandler); // ERROR, compiler can not deduce
while specificly write out the type is OK:
RegisterCmdHandler<Foo, Bar>(1, SomeHandler); // OK, compiler can deduce
but the latter has ugly API. How can I get the first on work?
How can I get the first on work?
Add an overload for plain function pointers:
template <typename ReqT, typename RespT>
bool RegisterCmdHandler(int cmd_id, int(*sync_handler)(int, const ReqT&, RespT&)) {
std::function<int(int, const ReqT&, RespT&)> sync_handler2(sync_handler);
return RegisterCmdHandler(cmd_id, sync_handler2);
}
How can I get the first on work?
I see some ways.
(1) If you can modify the RegisterCmdHandler() function and you don't need to know, inside it, what types ReqT and RestT are, I suggest you to avoid at all std::function and accept sync_handler as a simple template type.
I mean
template <typename F>
bool RegisterCmdHandler (int cmd_id, F sync_handler) {
// ... do something with sync_handler
return true;
}
This is a very flexible solution because F can be a function, a function pointer, a std::function, a lambda (also a generic lambda, so this solution is more flexible than using std::function), another type of class/struct with an operator(), a value returned from a std::bind. In short: a generic callable.
(2) If you can modify the RegisterCmdHandler() function but you need to know (and use) the ReqT and RestT, you can follows the plain function pointer way (see Maxim Egorushkin's answer for the syntax). Unfortunately this works with function pointers only and doesn't works (by example) when sync_handler is a lambda.
(3) If you can't modify RegisterCmdHandler() but you can use C++17, you can use std::function deduction guides and call the function as follows
RegisterCmdHandler(1, std::function{SomeHandler});
or, maybe better if you have to call it in different places, call it through a converter
template <typename F>
auto CallRegisterCH (int cmd_if, F && func)
{ return RegisterCmdHandler(cmd_if, std::function{std::forward<F>(func)}); }
calling it as follows
CallRegisterCH(1, SomeHandler);
(4) if you can't modify RegisterCmdHandler() and you have to use C++11 or C++14... well... explicating the template types
RegisterCmdHandler<Foo, Bar>(1, SomeHandler);
seems to me the better way.
Other ways you can explicit the std::function
std::function<int(int, Foo const &, Bar &)> sh{ SomeHandler };
RegisterCmdHandler(1, sh);
but seems to me almost the same thing.
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);
}
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?