c++ template parameter compiler can not deduce - c++

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.

Related

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.

Why could not deduce template argument when passing lambda instead of function pointer

I have a bubble-sort function that takes an array, a compare function, and a boolean that indicates if it should sorts the array upside-down. It is a template function that supports any data-type, and will deduce array size automatically.
When specifying the compare function, if I pass function pointer, the compiler will deduce the data type of array automatically, which is great. But if I pass a lambda instead, it will not deduce automatically. I have to specify the data type explicitly, or static_cast the lambda as fnCompare_t<double>.
What is the reason behind this? Because according to this post, as long as the lambda doesn't capture, it can be used like the plain-old function pointer, but it seems it is not always the case? How come it can be different in this case?
#include <iostream>
using namespace std;
template <typename T>
using fnCompare_t = int(*)(T const &, T const &);
template <typename T, size_t count>
inline void BubbleSort(
T(&array)[count],
fnCompare_t<T> fnCompare,
bool reverse)
{
cout << "TODO: Implement BubbleSort" << endl;
}
double doubleArray[] = {
22.3, 11.2, 33.21, 44.2, 91.2, 15.2, 77.1, 8.2
};
int CompareDouble(double const & a, double const & b)
{
return a > b ? 1 : a == b ? 0 : -1;
}
int main()
{
auto fnCompare = [](double const & a, double const & b) -> int {
return a > b ? 1 : a < b ? -1 : 0;
};
// compile OK:
BubbleSort(doubleArray, CompareDouble, false);
BubbleSort(doubleArray, static_cast<fnCompare_t<double>>(fnCompare), false);
BubbleSort<double>(doubleArray, fnCompare, false);
// compile error, could not deduce template argument:
//BubbleSort(doubleArray, fnCompare, false);
return 0;
}
The reason why is because you can't get an implicit conversion on a templated parameter when using deduction. The classic example is:
template <class T>
T min(T x, T y);
Calling this function as min(1, 3.0) will result in a failure. Because for both arguments, it tries to find a T to get a perfect match, and fails. If you specify the template parameter explicitly it can work: min<double>(1, 3.0). The same is true in your example, if you specify T explicitly it will work.
The idiomatic way to write the signature for your function is:
template <typename Iter, typename F>
inline void BubbleSort(
Iter b, Iter e,
F fnCompare,
bool reverse)
However, this discards the compile time length information. If you want to keep that, you can do:
template <typename T, size_t count, typename F>
inline void BubbleSort(
T(&array)[count],
F fnCompare,
bool reverse);
Though you should at least consider using std::array instead of a C style array which will make the signature a bit less ugly and has other benefits.
This may seem odd as we are not "verifying" the comparator having the correct signature, in the signature of our sort. But this is normal in C++, if the comparator is incorrect then it will fail at the point of usage and it will still be a compile time error. Note as well when you try to depend on a lambda implicitly converting into a function pointer, you are being unnecessarily restrictive: lambdas only convert into function pointers with identical signature. Even if the output of the lambda is implicitly convertible to the output of the function pointer, your lambda will not implicitly convert, even though the lambda can still be used!
As a final final note, it's usually better to pass functors because it's better for performance. Comparators are usually small functions and often will get inlined. But in your version, the comparator will not typically be inlined, in mine it will (because I preserve the original type of the lambda, and you do not).
You need to explicitly cast the lambda to a function pointer. There is no other way around it. But, instead of static_casting you can apply + to the lambda, which would trigger the function pointer conversion, as you can apply + to a pointer type:
BubbleSort(doubleArray, +fnCompare, false);
// ^^
// applying unary + invokes the function pointer conversion operator
The reason for why there is no implicit call to the conversion operator is that during overload resolution, the compiler will only consider templates that match perfectly (see this for more info). Because a lambda is not a function pointer, there cannot be a perfect match, and the overload is discarded.

Cast unresolved template overloaded class member

I have a class with two overloaded members. One takes an integer, the other one is a template function taking one argument.
class MyClass
{
public:
void doSomething(int data){ std::cerr << data;}
template <typename T> doSomething(T &&data){ std::cerr << data;}
};
I want to bind this function
MyClass myobject;
auto my_bind = std::bind(&MyClass::doSomething, &myobject, 2);
my_bind();
But this doesn't compile since the compiler cannot deduct which function to call.
error: no matching function for call to 'bind(unresolved overloaded function type, MyClass*, int)'
I understand that I have to cast the function.
auto my_bind2 = std::bind(static_cast<void (MyClass::*)(int)>(&MyClass::doSomething), &myobject, 2);
And now it compiles and works as expected.
But what if I want to bind it with any other parameter to call the template function? Is this even possible? I cannot find the syntax.
This doesn't work:
auto my_bind3 = std::bind(static_cast<void (MyClass::*)(std::string)>(&MyClass::doSomething), &myobject, std::string("Hello"));
I would like to avoid using lambdas. I simplified the code for MCVE but in the actual code I am supposed to use member pointers.
Thank you for the help
You should simply provide the address of a relevant instantiation of the member function-template:
auto my_bind3 = std::bind(&MyClass::doSomething<std::string&>, &myobject, std::string("Hello"));
//Note the reference here ^^
The above works because std::bind copies its arguments, and since the doSomething member function template takes its argument by a forwarding reference, we need to take advantage of reference collapsing.
While something like the snippet below will "bind",
auto my_bind3 = std::bind(&MyClass::doSomething<std::string>, &myobject, std::string("Hello"));
//Note the absence of a reference here ^^
It is going to fail when you eventually call my_bind3().
Demo
If you can use a lambda, use it, because perfect forwading will kick in.
auto my_bind4 = [&myobject](){ myobject.doSomething(std::string("\nHullo")); };

Lambda as template function

I have a very strange problem. To keep things simple, lets say I want to have a function which takes 2 functions with the same declaration as arguments
template<typename Func>
void foo(Func a, Func b)
{
std::cout << "good";
}
To try things out I took putchar from cstdio, and created an identical function to match the putchar.
int myPutcharFunc(int)
{
return 0;
}
int main()
{
auto myPutcharLambda = [](int) -> int
{
return 0;
};
foo(putchar, myPutcharFunc); // okay
foo(putchar, myPutcharLambda); //deduced conflicting types for parameter 'Func' ('int (__attribute__((__cdecl__)) *)(int)' and 'main()::<lambda(int)>')
}
Now, the lambda does not want to compile (the key is I want to use lambda capture).
So lets add template specialization, because the programmer is wiser than the machine, right? :)
template<typename Func>
void foo(Func a, Func b)
{
std::cout << "good";
}
template<>
void foo(int(*)(int), int(*)(int))
{
std::cout << "good";
}
No luck, the same error - why?
But for some reason, when I comment out the template specialization:
//template<>
void foo(int(*)(int), int(*)(int))
{
std::cout << "good";
}
The code compiles. I obviously do not want to overload foo for EVERY set of function's arguments - thats what templates are for. Every step was tested both with msvc++ and g++. What am I doing wrong?
Two possibilities.
1: Just put + in front of the lambda:
foo(putchar, +myPutcharLambda);
That works because unary + expects an integer-like value, such as a pointer. Therefore, the lambda converts to a function pointer.
Ultimately a (non-capturing) lambda doesn't have the same type as a function pointer, even though it's willing to convert to a function pointer.
How is a compiler supposed to know which conversions are allowed to make two objects of the same type?
2: There is another option, making use of the fact that the ?: is willing to do some conversions, converting one type to another in some circumstances.
template<typename Func1, typename Func2>
void foo2(Func1 a, Func2 b)
{
using common_type = decltype(true?a:b); // or 'false', it doesn't matter
foo<common_type>(a,b);
}
Every lambda is a different type, so you'll need to have two different template parameters to get them
template<typename FuncA, typename FuncB>
void foo(FuncA a, FuncB b)
Types don't decay when deducing template types (SEE COMMENT FOR CORRECTION). So a lambda remains a lambda and doesn't decay to a function pointer. Same reason a string literal is deduced as a char[N] instead of a const char *.
With your second example using specialization, it doesn't want to use your specialization, since the lambda is not a function pointer. You can cast the Lambda to a function pointer and make it work: https://godbolt.org/g/ISgPci The trick you can do here is say +my_lambda because + is defined for pointers so it will force the non-capturing lambda to become a function pointer.
A lambda has its own type which can decay to a function pointer but not in the case of a template function match, it will for the real function as you found because of the implicit conversion.
In the case of matching to a template you need to disambiguate and explicitly instantiate foo with the type you want or convert the lambda to a function pointer.
foo<decltype(putchar)>(putchar, myPutcharLambda);
or
foo(putchar, +myPutcharLambda);

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