I need to override connection between boost::signals2::signal and boost::function.
For this purpose I've created following template function:
template<typename T>
void bind(boost::signals2::signal<T> &signal, boost::function<T> function) {
// override code ...
}
I want to make use of this bind as simple as it can be.
From what I've read in posts on similar issues, template parameter should be deduced from function arguments.
But in my case when there's no explicit parameter it's not working.
boost::signals2::signal<void ()> my_signal;
bind<void ()>(my_signal, boost::bind(&A::func, this)); // this works
bind(my_signal, boost::bind(&A::func, this)); // error: no matching function for call
Am I missing something?
Can there be any workaround to avoid explicit template parameter?
The second argument type is not std::function<T>, but some bind type, so the compiler is unable to deduce the T for the second function parameter. You need to tell the compiler "You are OK with not finding a type for T in the second function parameter". This can be done by making the second parameter a non-deduced context.
template<typename T>
void bind(boost::signals2::signal<T> &signal,
typename std::common_type<boost::function<T>>::type function) {
// override code ...
}
Related
I have a function template as in the following minimal snippet. I want to test if it is callable before making the call:
#include <concepts>
void f(auto a) {}
// void f(int a) {} // This is ok
static_assert(std::invocable<decltype(f), int>);
But it does not compile with error
error: 'decltype' cannot resolve address of overloaded function
Alternatively,
void f(auto a) {}
template <auto F> concept callable = requires { {F(27)}; };
static_assert(callable<f>);
gives error
error: unable to deduce 'auto' from 'f'
Is this a limitation of C++20 language? Is there any way to force an instantiation of f<int>? Are there alternatives to make the check compile without changes to f?
A function template can only perform template argument deduction if you call it. So if you're not calling it, the only things you can do with a function template name is give it the template parameters you want (thus resolving into an actual function).
I'm trying to limit template deduction only to objects from the same hierarchy.
The code below compiles
template<class T>
void RegisterAction(const string& actionId, bool(T::*f)())
{
m_actions.emplace(actionId, std::bind(f, static_cast<T*>(this)));
}
but this code doesn't
template<class T, typename std::enable_if_t<std::is_base_of<BaseClass, T>::value>>
void RegisterAction(const string& actionId, bool(T::*f)())
{
m_actions.emplace(actionId, std::bind(f, static_cast<T*>(this)));
}
m_actions is of type std::unordered_map<string, std::function<bool()>>
Here is the error from Visual Studio
'BaseClass::RegisterAction': no matching overloaded function found
error C2783: 'void BaseClass::RegisterAction(const string &,bool (__cdecl T::* )(void))': could not deduce template argument for '__formal'
This is how you would use the method:
void DerivedClass::InitActions()
{
RegisterAction("general.copy", &DerivedClass::OnCopy);
RegisterAction("general.paste", &DerivedClass::OnPaste);
}
Btw, I can't use static_assert because there I'm using c++14.
Doesn't anyone has any idea?
You're trying to introduce a new template parameter in order to cause a substitution error---which is correct---but your syntax is slightly incorrect. What you should write is:
template<class T, typename = std::enable_if_t<std::is_base_of<BaseClass, T>::value>>
// ^^^ this equal sign is crucial
When you write typename = foo, you're declaring an unnamed type template parameter (it's like writing typename unused = foo) and making the default value for that type foo. Thus, if someone tries to instantiate this template with T not derived from BaseClass, a substitution failure occurs in the default argument, causing deduction to fail.
Since you wrote it without the equal sign, typename std::enable_if_t<...> was interpreted as a typename-specifier, that is, the compiler thinks you're declaring a non-type template parameter whose type is typename std::enable_if_t<...>, which you have left unnamed. Consequently, when T is derived from BaseClass, the type of this template parameter is void. Since non-type template parameters cannot have type void (as there are no values of type void), a SFINAE error occurs here.
Interestingly, both GCC and Clang also fail to give a useful error message. They also complain that the unnamed template argument cannot be deduced, rather than pointing out that void non-type template parameters are invalid (or even just pointing out that it is a non-type template parameter of type void).
When it is intended to use RegisterAction to register only for those classes that are derived from BaseClass, is seems that it is better to state with static_assert explicitly why some T cannot be used with RegisterAction instead of just "hiding" the problem with SFINAE.
So
template<class T>
void RegisterAction(const string& actionId, bool(T::*f)())
{
static_assert(
std::is_base_of<BaseClass, T>::value,
"T shall be derived from BaseClass for RegisterAction to work"
);
m_actions.emplace(actionId, std::bind(f, static_cast<T*>(this)));
}
will scream loudly and clearly about exact reason of why RegisterAction cannot accept some actions.
Look at this (simplified) example:
int foo(int) { return 0;}
double foo(double) { return 0.0; }
template <class T>
enable_if<is_integral<T>::value>
bar(T(*f)(T)) {}
int main()
{
bar(foo);
return 0;
}
My expectation was that the compiler will first try to instantiate the template for each overload, which would fail (SFINAE) for the second overload and therefore there would be only void bar(int(*f)(int) left in the set of candidates, which would resolve using the first overload of foo. That is not what happens. It fails with something like this:
no matching function for call to ‘bar(<unresolved overloaded function type>)’
couldn't deduce template parameter ‘T’
Is there any way to achieve something like this?
C++'s type deduction is very simplistic. The clauses [temp.deduct.call]/6.2 and [over.over]/1 describe ways in which an overloaded name can be used as an argument.
In your example, both the deductions would succeed (to T=int and T=double) and one substitution would fail. But the language requires only one deduction succeed.
You asked how to achieve it. Here are some options:
Do not use an overloaded name, static_cast to the desired function type, or explicitly provide the type of T in the call to bar.
Alter the overloads such that deduction will succeed for only one of the overloads. In other words, exclude overloads by deduction, not by substitution. See below for an example.
Add another parameter which can deduce T.
Pass the buck and avoid deducing T until a later point.
Example for #2:
tempate<class T> struct integral_wrapper { T t; }
integral_wrapper<int> foo(int);
double foo(double);
template<class T>
void bar(integral_wrapper<T> foo(T));
Example for #3:
template<class T>
void bar(T (*f)(T), T);
bar(foo, 0);
Example for #4:
struct foo_t
{
int operator()(int);
double operator()(double);
} foo;
template<class F>
void bar(F);
bar(foo);
Note that a generic lambda can be another approach for #4.
Depending on your usage, some of these may be more attractive than others. Approach #4 is particularly useful when passing arguments to STL algorithms.
I'm experimenting with resolving the address of an overloaded function (bar) in the context of another function's parameter (foo1/foo2).
struct Baz {};
int bar() { return 0; }
float bar(int) { return 0.0f; }
void bar(Baz *) {}
void foo1(void (&)(Baz *)) {}
template <class T, class D>
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
int main() {
foo1(bar); // Works
foo2<Baz>(bar); // Fails
}
There's no trouble with foo1, which specifies bar's type explicitly.
However, foo2, which disable itself via SFINAE for all but one version of bar, fails to compile with the following message :
main.cpp:19:5: fatal error: no matching function for call to 'foo2'
foo2<Baz>(bar); // Fails
^~~~~~~~~
main.cpp:15:6: note: candidate template ignored: couldn't infer template argument 'D'
auto foo2(D *d) -> void_t<decltype(d(std::declval<T*>()))> {}
^
1 error generated.
It is my understanding that C++ cannot resolve the overloaded function's address and perform template argument deduction at the same time.
Is that the cause ? Is there a way to make foo2<Baz>(bar); (or something similar) compile ?
As mentioned in the comments, [14.8.2.1/6] (working draft, deducing template arguments from a function call) rules in this case (emphasis mine):
When P is a function type, function pointer type, or pointer to member function type:
If the argument is an overload set containing one or more function templates, the parameter is treated as a non-deduced context.
If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set the parameter is treated as a non-deduced context.
SFINAE takes its part to the game once the deduction is over, so it doesn't help to work around the standard's rules.
For further details, you can see the examples at the end of the bullet linked above.
About your last question:
Is there a way to make foo2<Baz>(bar); (or something similar) compile ?
Two possible alternatives:
If you don't want to modify the definition of foo2, you can invoke it as:
foo2<Baz>(static_cast<void(*)(Baz *)>(bar));
This way you explicitly pick a function out of the overload set.
If modifying foo2 is allowed, you can rewrite it as:
template <class T, class R>
auto foo2(R(*d)(T*)) {}
It's more or less what you had before, no decltype in this case and a return type you can freely ignore.
Actually you don't need to use any SFINAE'd function to do that, deduction is enough.
In this case foo2<Baz>(bar); is correctly resolved.
Some kind of the general answer is here: Expression SFINAE to overload on type of passed function pointer
For the practical case, there's no need to use type traits or decltype() - the good old overload resolution will select the most appropriate function for you and break it into 'arguments' and 'return type'. Just enumerate all possible calling conventions
// Common functions
template <class T, typename R> void foo2(R(*)(T*)) {}
// Different calling conventions
#ifdef _W64
template <class T, typename R> void foo2(R(__vectorcall *)(T*)) {}
#else
template <class T, typename R> void foo2(R(__stdcall *)(T*)) {}
#endif
// Lambdas
template <class T, class D>
auto foo2(const D &d) -> void_t<decltype(d(std::declval<T*>()))> {}
It could be useful to wrap them in a templated structure
template<typename... T>
struct Foo2 {
// Common functions
template <typename R> static void foo2(R(*)(T*...)) {}
...
};
Zoo2<Baz>::foo2(bar);
Although, it will require more code for member functions as they have modifiers (const, volatile, &&)
Goal : Obtain a callback function that will take any type of parameters as the callback function's parameters
.h
template <typename F, typename A>
void DelayedCallback(F&& CallbackFunction, A&& Args = NULL);
/
.cpp
void DelayedCallback(F&& CallbackFunction, A&& Args)
{
//Timer function that received a function pointer to be the "callback" function
Timer((std::bind(std::forward<F>(CallbackFunction), std::forward<A>(Args)))())
}
/
DelayedCallback(&HUDExit);
void HUDExit() {}
/
ERROR : DelayedCallback(FName,float,F &&,A &&)' : could not deduce template argument for 'A'
What am I doing wrong? I'm new to most of these concept in c++, more of c# programmer
EDIT : It's not only about the error, I'm pretty sure it's not the only one I am making.
Your error message doesn't match the signature of DelayedCallback
template <typename F, typename A>
void DelayedCallback(F&& CallbackFunction, A&& Args = NULL)
DelayedCallback(&HUDExit);
That function signature and the usage you've shown will not produce an error message that says
ERROR : DelayedCallback(FName,float,F &&,A &&)' : could not deduce template argument for 'A'
But ignoring the template parameter mismatches, the code you've shown will also result in a similar error. The problem is that template parameters cannot be deduced from default arguments and A is treated as a non-deduced context in your example.
From N3337, §14.8.2.5/5 [temp.deduct.type]
The non-deduced contexts are:
...
— A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
Instead, you should change A to a parameter pack. That'll allow you to pass zero or more arguments to DelayedCallback.
template <typename F, typename... A>
void DelayedCallback(F&& CallbackFunction, A&&... Args)
{
//Timer function that received a function pointer to be the "callback" function
Timer((std::bind(std::forward<F>(CallbackFunction), std::forward<A>(Args)...))())
// previous line is missing a semicolon at the very least
}
Once you fix all that, you'll run into the problem mentioned in the comments. You cannot split the declaration and definition of a function template between a header and source file as you would with a non-template. So implement DelayedCallback in the header itself as I have done above.