Compilation error when passing variadic args to a template function - c++

I have the following template method:
template<typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args) {
auto func = std::bind(&T::template create<Window*, Args&...>, std::placeholders::_1, std::forward<Args>(args)...);
_store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
auto ret = func(window);
ret->set_name(name);
return ret;
});
}
Essentially all I need to do is bind the variadic Args to T::create (which itself is a static variadic template method), but allow populating the first argument (window) separately when it's called.
The above code fails with the following error
error: no match for call to ‘(const std::_Bind<std::shared_ptr<{anonymous}::SceneWithArgs> (*(std::_Placeholder<1>, const char*))(smlt::Window*&, const char (&)[4])>) (smlt::Window*&)’
auto ret = func(window);
~~~~^~~~~~~~
When calling the code like this:
manager.register_scene<SceneWithArgs>("test", "arg");
I don't really understand the error, or how to fix it.
I initially solved this by simply calling create inside the lambda, and this works on GCC 4.9 and above, but I have to remain compatible with GCC 4.8.4 and there's a bug which prevents using variadic args inside a lambda :(
UPDATE
OK so adding std::decay (as seen in the comments) didn't fix the problem entirely, the first argument kept deducing to Window*&& rather than Window*, but actually specifying the type of func (e.g. std::function<SceneBasePtr (Window*)>) instead of using auto made things compile.
I'm not sure why that is though...

You did not show the declaration of create, but I assume it looks as follows:
template <typename... Args>
static SceneBasePtr create(Args&&...);
It looks good. You use forwarding references, so that the compiler can deduce the exact types of arguments. As long as the compiler deduces the types, this will work. However...
&T::template create<Window*, Args&...>
Here, you instantiate the function template explicitly. That is, the compiler will no longer deduce the types, instead, it will replace the template parameters by the template arguments you provide. What are these template arguments?
template <typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args);
//...
manager.register_scene<SceneWithArgs>("test", "arg");
The first one is passed explicitly, it's Window*, this one is obvious. Now Args... -- "test" and "arg" are raw string literals, and their corresponding types are const char[5] and const char[4], respectively. Then Args... (after a deduction process) become const char(&)[5] and const char(&)[4]. Then what happens now, when you instantiate create with these types? You will end up with a declaration as below:
static SceneBasePtr create(Window*&&, const char(&)[5], const char(&)[4]);
Note that reference collapsing rules turn the first parameter into an r-value reference, while the rest are l-value references.
std::bind deduces types as well. However, std::bind will want to store the arguments, so that they can be reused later. The problem is that std::bind cannot store const char[5]. Instead, it will decay the type of each argument. This means that each raw string literal will become const char*, and will be passed as an l-value argument of this type to the bound function, and this does not match the manually instantiated create. A similar problem is with the window argument. It is deduced as an l-value, however, the manually instantiated create function expects an r-value.
It's best not to explicitly specify template types of a function template. If for some reasons (bug?) you cannot use args... inside the lambda, you can generate a proper create signature:
&T::template create<Window*&, typename std::decay<Args>::type&...>
// ~^~ ~~~~~~~~~^
To end up with:
static SceneBasePtr create(Window*&, const char*&, const char*&);

Related

Distinguish between function references/pointers accepting const and non-const argument with same name as function parameter

Let's consider two functions with same names:
int func(int&)
{
return 7;
}
int func(const int&)
{
return 5;
}
Let int mutableValue = 5 be defined somewhere. Is there any possibility that template function call in call(mutableValue, func) would take only int func(int&)?
I don't want to use static_cast<int(&)(int&)> - because it's so noisy.
Naive implementation:
template<typename Arg, typename Ret>
void call(Arg& val, Ret (&fun)(Arg&))
{
fun(val);
}
works on gcc, but does not work on clang - solutions must work on both compilers.
Accepting only const Arg& is easy. Deleting const Arg& overload does not help.
I believe clang is the only compiler that gets this right. This should not compile. As long as the argument to fun is an overload set that does not contain any function templates, template argument deduction will be attempted for each member of the overload set. If template argument deduction would succeed for more than one of these overloads, the function parameter becomes a non-deduced context [temp.deduct.call]/6.2.
In the example in question, the argument to fun is the overload set func, which does indeed not contain any function templates. Thus, argument deduction on fun is attempted for both overloads of func, which succeeds. As a result, the parameter fun becomes a non-deduced context, which means that no argument can be deduced for Ret and the call fails as there are no candidates (exactly what clang complains about).
To disambiguate this call, simply explicitly specify the argument for the first template parameter:
call<int>(mutableValue, func)
Since it seems to be impossible to resolve the ambiguity in one template argument deduction:
If you are ok with a change of the syntax at the call site you can separate the call into two calls/deduction passes:
template<typename Arg>
auto call(Arg& val)
{
return [&](auto (&fun)(Arg&)){ fun(val); };
}
to be called as
call(mutableValue)(func)
Another downside is however that the lambda could be stored by a caller and accidentally used later when the captured reference isn't valid anymore.
You could maybe hide this in a macro call, so that the syntax matches what you want and to reduce the potential for misuse.

Conflicting types for variadic parameter

I am trying to write a general invocation function.
It has the following syntax:
template<int Index, typename ReturnType, typename... Parameter>
ReturnType invokeGlobalFunction(Parameter... parameters)
{
return invocator->invoke<ReturnType>(Index, parameters...);
}
Next, I try to derive two different function points from it, like this:
registerFunction(::someGlobalFunction, &invokeGlobalFunction<0, void>);
registerFunction(::someOtherFunction, &invokeGlobalFunction<1, int>);
Where someGlobalFunction has the prototype void someGlobalFunction() and someOtherFunction has the prototype int someOtherFunction(int, const char *).
On the first call, it works like a charm, however the second call throws the error: candidate template ignored: deduced conflicting types for parameter 'Parameter' (<int, const char *> vs. <>).
This implies, that the compiler (g++ 7.4.0 on an Ubuntu system btw.) does not overload the invokeGlobalFunction with the different parameter sets like I expected him to.
A note: When I explicitly set the parameter types on the call
registerFunction(::someOtherFunction, &invokeGlobalFunction<1, int, int, const char *>);
the compiler happily takes it, but I'd like to avoid that, if possible.
As a bonus, it would be great, if I could somehow create a unique function each time the index changes, because that would allow me to have functions with identical parameters but differing return types (which is illegal as far as I know).
Thank you.
but I'd like to avoid that, if possible.
Not with template functions, as far I know.
The problem is that a template parameter isn't a single object but a set of object where a function can accept only an object from the set.
When you write
&invokeGlobalFunction<1, int>
you choose a precise function with Index = 1, ReturnType = int and (this is the point) an empty Parameter... list.
Suggestion: if you can, transform invokeGlobalFunction() in a template struct with a template method.
Something as
template <int Index, typename ReturnType>
struct invokeStruct
{
template <typename ... Parameters>
ReturnType operator() (Parameters ... parameters)
{
// ...
}
};
This way you have a set of struct with, in every struct, a set of operator() in it; passing a invokeStruct<1, int>{} as argument, you pass a single object but, inside it, you have available a set of method.

Pass function pointer into Variadic template function

I have a function printint which take an integer as parameter. How I can pass it into a variadic template as below? I have try run_callback(printint,1) but it says "no instance match with the function". Please help, thank you!
void printint(int i)
{
}
template <typename ...Args>
void run_callback(const std::function<void(Args...)>& func, Args ...as)
{
....
}
When you call your function template, e.g., like so
run_callback(printint, 1);
the compiler will try to deduce template arguments which, when substituted into the function parameter types, will make the function parameter types match the types of the arguments. The problem here is that there are no Args... that you could put into const std::function<void(Args...)>& to make that type match the type of printint, which is void(int). Therefore, template argument deduction fails.
If you just want your run_callback to use any function with a signature that matches Args..., have it take a reference to a function with such signature:
template <typename... Args>
void run_callback(void (&func)(Args...), Args... as)
{
}
live example
This, however, will be somewhat brittle as it basically requires that the types in Args are exact matches for the parameter types of the callback. Most likely, you'd want run_callback to work with any function (or, more general, any callable) that could be called with the given args. One way to achieve this would be to have your function template accept any type as a callback but only enable the overload when the func parameter is actually a callable that is invocable using the respective argument list:
template <typename F, typename... Args>
auto run_callback(F&& f, Args&&... as) -> std::enable_if_t<std::is_invocable_v<F, Args...>>
{
f(std::forward<Args>(as)...); // call the callback
}
live example
Finally, if, for some reason, you really absolutely need your func parameter the be an std::function, you could hide the Args... parameter pack in func in a non-deduced context:
template <typename... Args>
void run_callback(const std::common_type_t<std::function<void(Args...)>>& func, Args... as)
{
}
live example
The trick here is the use of std::common_type_t<T>, which really is just a shorthand for std::common_type<T>::type, which will just come out to be T again in the end (Note: there's nothing special about std::common_type except that it's already there for us to use; we could use any other helper template; all we need is something that forwards its argument to a dependent name). It's impossible in general to unambiguously deduce which T you'd have to plug into C<T>::xyz to make C<T>::xyz become a certain type. It's not even guaranteed that there is such an xyz for every single T or that C<T>::xyz would be a type to begin with. For this reason, the nested-name-specifier in a qualified-id is defined to be a non-deduced context [temp.deduct.type]/5.1. Long story short, that means that Args... now appears in the type of the parameter func in such a way that the compiler will not try to deduce what Args... should be from an argument passed for the func parameter (i.e., it will not try to do the thing that caused your original code to fail). It still can deduce Args... from the as function parameter pack, however. Thus, type deduction will just make Args... be whatever the types of the arguments passed for as... are and succeed. After type deduction succeeded, the deduced Args... are substituted back into the remaining parameters and the type of func will come out to be const std::function<void(Args...)>& but now with the types of Args... taken from what they were deduced to be from the as parameter pack.
Another way to do basically the same thing would be to, e.g., wrap the argument for the first parameter in an initializer list:
run_callback({ printint }, 1);
live example
An argument that is an initializer list also makes the parameter a non-deduced context in this case [temp.deduct.type]/5.6, so the explanation for why this works is basically the same as in the previous example. Note that, while the previous approach solves the problem from within the template declaration, this approach solves it at the site of the function call.

Visual Studio issues error because of function overload [duplicate]

I am currently working on a library where I am chaining function objects.
I am creating a function template that takes a callable object (std::function at the moment) and is parametrized on the output and input type of the function. Here is a simplified version of what I am defining:
template <typename In, typename Out>
std::vector<Out> process(std::vector<In> vals, std::function< Out(In) > func)
{
// apply func for each value in vals
return result;
}
The problem I am having is on usage. It seems that when I pass a lambda, the compiler cannot deduce the type correctly, so complains that the function doesn't exist:
std::vector<string> strings;
// does NOT compile
auto chars = process(strings,
[]( std::string s ) -> char
{
return s[0]; // return first char
}
);
If I explicitly wrap the lambda in std::function, the program compiles:
std::vector<string> strings;
// DOES compile
auto chars = process(strings,
std::function< char(std::string) >(
[]( std::string s ) -> char
{
return s[0]; // return first char
})
);
I haven't tested passing function pointers or function objects yet, but it seems like it will be difficult for the compiler to deduce the the In and Out parameters if I'm not directly passing the explicit std::function object.
My question is this: is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?
Perhaps parametrize the template on the function type instead of the input/return types? Essentially I need to deduce the In and Out type for an arbitrary callable. Perhaps some kind of auto/decltype trick for the return type of the template function?
Thank you.
I think what you can do is to create an intermediate return type deducing function which uses decltype to determine the arguments to be passed to the actual function object:
template <typename Out, typename In>
std::vector<Out> process_intern(std::vector<In> vals, std::function< Out(In) > func)
{
// whatever
}
template <typename In, typename Func>
auto process(std::vector<In> vals, Func func) -> std::vector<decltype(func(vals[0]))>
{
return process_intern<decltype(func(vals[0]))>(vals, func);
}
Of course, you may want to consider implementing the logic in process() directly anyway unless there is a reason to type erase the function type.
is there a way to get around this, so that I can deduce the input/return type of a callable object without explicitly mentioning them at the call site?
No. User-defined conversions are not considered in template argument deduction. The compiler have to come up with In and Out such that the type of the parameter and the type of the argument have to match (almost) exactly, but they never will in this case.
Perhaps parametrize the template on the function type instead of the input/return types
Yes, that's what is normally done (take a look at the standard library algorithms, for example)

Generic way to deduce the return type of a functor?

This question is a follow-up of How to deduce the type of the functor's return value?
I'm reformulating it in a more abstract way.
Given the pseudocode of a template function
template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>)
{
// do something
// ............
return fn(<ret-expr>)
}
where <ret-expr> is an arbitrary expression which involves arg, what shall I use for <decl-expr> to set the return type of ComputeSomething equal to the return type of the functor.
The functor may be a class, a lambda or a function pointer.
Partial solutions I found so far.
(a) The answer for my linked question done by ecatmur. Essentially, it is repeating the return statement in <decl-expr>. Problems: it is error-prone and wouldn't work if contains local variables.
(b) It works only for function pointers
template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, Ret(*fn)(Arg))
(c) It assumes that the argument of the functor is of type Arg (which may not hold in general) and requires Arg to be default-constructible
template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(Arg())
(d) Using std::declval which is supposed to lift the default-constructible restriction, as suggested in how to deduce the return type of a function in template. Could anybody explain how it works?
template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(std::declval<Arg>())
Use result_of. It is backwards compatible and takes all the ugly declval pain out of your code. You still need to remember to add rvalue reference qualifiers (&&) if you actually just forward values.
Something else I find important: Your function forwards arguments to another function. In such cases you should always use rvalue references to pass the arguments.
If all you are trying to do is improve maintainability: there are several attempts at a RETURNS macro around that try to minimize the repetition between the return type declaration and the actual return expression, but I haven't seen any that allows a function body that contains more than the actual return statement.
As for how declval works: Its compiler dependent. It isn't allowed to occur in an evaluated content and its argument can be an incomplete type. See 20.2.4
std::declval is a function template that is only declared (not defined). It can thus only be used in unevaluated contexts such as the argument to sizeof and decltype. It is declared to return an rvalue of the specified type. This allows you to use it to manufacture a dummy parameter for a function call in a decltype expression.
e.g.
typedef decltype(fn(std::declval<Arg>())) t;
declares t to be the type of the result of calling fn with an rvalue of type Arg. This is similar to your case (c) (fn(Arg())), but it doesn't require anything of Arg, so it works on types without default constructors.
If your return expression uses a local variable of type foo, then you can use decltype(fn(std::declval<foo>())), again regardless of how you construct a foo.
If you need an lvalue, such as a named object or an lvalue reference, then you can use std::declval<foo&>(). This allows you to handle the case where the type depends on whether you have an lvalue or an rvalue.
Here's my own solution, the best I could get
template <typename Arg, typename Fn>
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn)
To make (c) works for anything, you need 2 overloads. 1st as shown in (c), 2nd:
template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, std::function<Ret(Arg)> fn)
Also, as gcc bug 54111 shows - deduction of return type is very unreliable.
A variant of (b) working not only with function pointers should be something like
template<typename Arg, typename Ret>
Ret ComputeSomething (Arg arg, function<auto (Arg) -> Ret> f)