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.
Related
using FuncDef = void(int a, int b);
template <typename R, typename... Args>
class WrongFunction {
public:
void F1(R(*Fn)(Args...)) {}//error: C2091 function returns function
};
template <typename T> class Function;
template <typename R, typename... Args>
class Function<R(Args...)> {
public:
void F1(R(*Fn)(Args...)) {}
};
void test() {
WrongFunction<FuncDef> errfunc;//trigger the above compilation error
Function<FuncDef> func;//no problem
}
(1) Why Function works but WrongFunction doesn't (check the compilation error in the comment)? What is the theory behind (e.g. something from cppreference which contains every details about C++)?
(2) (optional) Is there a way to make WrongFunction work but not in the form as Function?
The follow class template:
template <typename R, typename... Args>
class WrongFunction {
public:
void F1(R(*Fn)(Args...)) {}//error: C2091 function returns function
};
defines a template parameter API which expects:
a return type R, and
a variadic number of arguments (of possible different types) Args.
Now, a template parameter pack may be empty, which hides your actual error here. In the instantiation of of the WrongTemplate class template:
WrongFunction<FuncDef> errfunc;
you provide only a single template argument, which matches the first template parameter of the class template:
FuncDef is used as argument for R in WrongFunction
whereas you are providing no argument for template parameter pack.
Thus, the compilers' error message is quite telling: you are instantiating the WrongFunction class template with a template argument provided for the template parameter R, which semantically signifies a return type, but with a function type argument. Meaning, in the associated specialization, R is a function type;
void F1(R(*Fn)(Args...)) {}
// resolves to, for WrongFunction<FuncDef> specialization
void F1( (void)(*)(int, int) (*Fn)() ) {}
// ^^^^^^^^^^^^^^^^^^^ <- R
thus defining a function parameter that is declared to have the type "pointer to function (named Fn) with zero arguments that return a pointer to function with two arguments that return void". This is illegal, thus the compiler error.
In your other class template, you leverage specialization, constructing the primary class template API to have a single type template parameter, semantically intended to be provided arguments that are of type "pointer to function", as the the only partial specialization (as the primary template is not defined) is specifically partially specialized for this kind of argument.
(2) (optional) Is there a way to make WrongFunction work but not in the form as Function?
The template parameter API of WrongFunction expects the client to explicitly provide the return type for the intended function pointer (of the member function) and the argument types by separate template arguments. It basically does not rely on deduction. Your second approach with specialization is the common one, and instead of fixing the likely ill-designed WrongFunction, I will note a similar but slightly different design of Function, which embeds a function pointer into its type instead. Thus could be useful if the member function of a given specialization should only use delegation to a specific function, rather than allowing dynamic dispatch delegation via function pointers of specific function pointer type (as function parameter to the member function).
#include <utility>
template <auto> class Function;
template <typename R, typename... Args, R (*Fn)(Args...)>
class Function<Fn> {
public:
template <typename... FnArgs /* optionally SFINAE that FnArgs is Args */>
void F1(FnArgs &&... args) {
Fn(std::forward<FnArgs>(args)...);
}
};
void f(int a, int b) {}
int main() {
Function<f> func;
func.F1(1, 2);
}
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.
This is similar to the question, but a more specific case. This time, no compiler work as expected.
template<class T>
struct nondeduced
{
using type = T;
};
template<class T>
using nondeduced_t = typename nondeduced<T>::type;
template<class... T, class U>
void f(void(*)(nondeduced_t<T>..., U)) {}
void g(int, char) { }
int main()
{
f<int>(g); // error?
}
In the above example, the parameter pack T cannot be deduced, but the compiler should be able to deduce U after explicit arguments substitution for pack T (i.e. single int in this case).
The above is expected to work without the nondeduced_t trick as well:
template<class... T, class U>
void f(void(*)(T..., U)) {}
Because the parameter pack T is already in non-deduced context according to
[temp.deduct.type]p5
The non-deduced contexts are:
A function parameter pack that does not occur at the end of the parameter-declaration-list.
Unfortunately, no compiler I tested (g++/clang) accept the code.
Notably something like below works on both g++ & clang.
template<class... T>
void f(void(*)(nondeduced_t<T>..., char)) {}
And again, this doesn't work on both:
template<class... T>
void f(void(*)(T..., char)) {}
Is my expectation wrong?
By [temp.deduct.type]p5 one of the non-deduced-context is
A function parameter pack that does not occur at the end of the parameter-declaration-list.
Parameter packs that doesn't appear as the last argument of a template functions are never deduced, but is completely right to specify the parameter types disabling the deduction. e.g
template<class T1, class ... Types> void g1(Types ..., T1);
g1<int, int, int>(1,2,3); // works by non-deduction
g1(1,2,3) // violate the rule above by non-deduced context
But changing the order of function argument even leaving the template parameters as they are, remove the non-deduced context condition and break the infinite expansion of parameter pack. e.g
template<class T1, class ... Types> void g1(T1, Types ...);
g1(1,2,3) // works because its a deduced context.
There're two reasons your code don't compile:
The order of function argument create a non-deduced-context which cause the type of the parameter pack T in the pattern stated in function f would never be deduced.
The template parameter T appears only as a qualifiers in function arguments(e.g nondeduced_t) and not directly specified as a function argument(which allow argument deduction).
To make the code compile you have either place the expansion of the parameter pack as it is forgetting the nondeduced_t indirect, as
template<class... T,class U>
void f( void(*)(U,T...) ) { }
f(g);
or changing the order of template parameters and specify the template argument on function call, as
template<class U,class... T>
void f( void(*)(U,typename nondeduced<T>::type...) ) {}
f<int,char>(g);
I have the following class:
class FunctionCallback
{
public:
static CallbackHandle Create(const std::function<void(void)> &function);
template<typename T,typename... TARGS>
static CallbackHandle Create(const std::function<T(TARGS...)> &function);
};
I then call 'Create' like this:
FunctionCallback::Create<void,float>([](float f) -> void {}); // Whether I use a lambda-function or a function pointer makes no difference
Even though that should be correct (?), visual studio underlines that line in read with the message:
Error: no instance of overloaded function "FunctionCallback::Create" matches the argument list
argument types are: (lambda []void (float f)->void)
However the program compiles fine in visual studio without any warnings or errors.
g++-5 is unable to compile it altogether with a similar message.
When changing it to:
FunctionCallback::Create<void,float>(std::function<void(float)>([](float f) -> void {}));
It doesn't display the message anymore and compiles both on windows and linux.
Why can't it deduce the type properly unless I explicitly specify it?
You almost never want to deduce the type of a type-eraser, that std::function is an example of. With a few exceptions, template type deduction attempts to deduce the exact type of an argument. For a std::function<T>-type parameter this means that the compiler can deduce T if the corresponding argument is a std::function as well. The type of a lambda expression is not a std::function.
Even if you explicitly specify type template arguments, the deduction still continues after corresponding types have been substituted with the arguments. In the case of a parameter pack, the compiler still tries to deduce the rest of arguments, so for FunctionCallback::Create<void,float> the compiler ends up with:
template <typename... TARGS>
static CallbackHandle Create(std::function<void(float, TARGS...)> function);
and this won't match anything else than std::function<void(float, TARGS...)> (or something that derives from this type); otherwise the deduction for TARGS fails.
If your goal is to always specify the parameter pack's types explicitly, you can put that pack (or the entire parameter) in a non-deduced context:
template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;
struct FunctionCallback
{
template <typename T, typename... TARGS>
static void Create(identity_t<std::function<T(TARGS...)>> function);
};
FunctionCallback::Create<void, float>([](float f) -> void {});
DEMO
In this case, however, you'll have to pay the price of type-erasure. Instead, you can consider accepting any function object type:, letting the compiler to deduce the exact type of the argument:
struct FunctionCallback
{
template <typename F>
static void Create(F function){}
};
FunctionCallback::Create([](float f) -> void {});
DEMO 2
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, &&)