I try to implement a class which accepts a function and a variadic argument list as input to execute these functions later on a worker thread. My current implementation has a problem, if one of the arguments is a reference.
Have a look at the following smaller code example:
#include <functional>
#include <iostream>
template<typename Result, typename ...Args>
Result Foo(Result f(Args...), Args... args)
{
return f(args...);
}
int BarValue(int x){return x;}
int BarPointer(int* x){ *x++;return *x; }
int BarRef(int& x){ x++; return x; }
int main()
{
int x{0};
std::cout << Foo(BarValue, x) << std::endl;
std::cout << Foo(BarPointer, &x) << std::endl;
std::cout << Foo(BarRef, x) << std::endl; // does not compile: Error 1
std::cout << Foo(BarRef, std::ref(x)) << std::endl; // does also not compile: Error 2
return 0;
}
Error 1:
<source>: In function 'int main()':
<source>:31:31: error: no matching function for call to 'Foo(int (&)(int&), int&)'
31 | std::cout << Foo(BarRef, x) << std::endl;
| ^
<source>:4:8: note: candidate: 'template<class Result, class ... Args> Result Foo(Result (*)(Args ...), Args ...)'
4 | Result Foo(Result f(Args...), Args... args)
| ^~~
<source>:4:8: note: template argument deduction/substitution failed:
<source>:31:31: note: inconsistent parameter pack deduction with 'int&' and 'int'
31 | std::cout << Foo(BarRef, x) << std::endl;
| ^
ASM generation compiler returned: 1
<source>: In function 'int main()':
<source>:31:31: error: no matching function for call to 'Foo(int (&)(int&), int&)'
31 | std::cout << Foo(BarRef, x) << std::endl;
| ^
<source>:4:8: note: candidate: 'template<class Result, class ... Args> Result Foo(Result (*)(Args ...), Args ...)'
4 | Result Foo(Result f(Args...), Args... args)
| ^~~
<source>:4:8: note: template argument deduction/substitution failed:
<source>:31:31: note: inconsistent parameter pack deduction with 'int&' and 'int'
31 | std::cout << Foo(BarRef, x) << std::endl;
|
^
Error 2:
<source>: In function 'int main()':
<source>:33:41: error: no matching function for call to 'Foo(int (&)(int&), std::reference_wrapper<int>)'
33 | std::cout << Foo(BarRef, std::ref(x)) << std::endl;
| ^
<source>:5:8: note: candidate: 'template<class Result, class ... Args> Result Foo(Result (*)(Args ...), Args ...)'
5 | Result Foo(Result f(Args...), Args... args)
| ^~~
<source>:5:8: note: template argument deduction/substitution failed:
<source>:33:41: note: inconsistent parameter pack deduction with 'int&' and 'std::reference_wrapper<int>'
33 | std::cout << Foo(BarRef, std::ref(x)) << std::endl;
| ^
ASM generation compiler returned: 1
<source>: In function 'int main()':
<source>:33:41: error: no matching function for call to 'Foo(int (&)(int&), std::reference_wrapper<int>)'
33 | std::cout << Foo(BarRef, std::ref(x)) << std::endl;
| ^
<source>:5:8: note: candidate: 'template<class Result, class ... Args> Result Foo(Result (*)(Args ...), Args ...)'
5 | Result Foo(Result f(Args...), Args... args)
| ^~~
<source>:5:8: note: template argument deduction/substitution failed:
<source>:33:41: note: inconsistent parameter pack deduction with 'int&' and 'std::reference_wrapper<int>'
33 | std::cout << Foo(BarRef, std::ref(x)) << std::endl;
| ^
Execution build compiler returned: 1
Compiled with gcc 10.2 and -O3 -std=c++17 : GodBolt
How can I solve this reference problem?
My recommendation is that you take a look at how the standard library itself uses templates to pass callable objects (functions, lambdas, etc.): By using a single template type:
template<typename Func, typename ...Args>
auto Foo(Func f, Args&&... args)
{
return f(std::forward<Args>(args)...);
}
Note that I have added a call to std::forward to properly "forward" the arguments.
Also note that I made the return-type auto to let the compiler deduce the return type.
If I understand your comments correctly, you want to create a variable that holds the returned value of f, and then return that variable later? Then you could either do it using decltype as you do in your compiler-explorer link, or just use plain auto again when defining and initializing your variable:
template<typename Func, typename ...Args>
auto Foo(Func f, Args&&... args)
{
auto value = f(std::forward<Args>(args)...);
// Do something with the variable value...
return value;
}
This will of course not work if the function f have a void return value.
Related
I'm writing a template wrapper function that can be applied to a functions with different number/types of arguments.
I have some code that works but I'm trying to change more arguments into template parameters.
The working code:
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS>
int wrapper(int (*func)(ARGS...), ARGS... args) { return (*func)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool>(func0, b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
Now I want int (*func)(ARGS...) to also be a template parameter. (It's for performance reasons. I want the pointer to be backed into the wrapper, because the way I'm using it prevents the compiler from optimizing it out.)
Here is what I came up with (The only difference is I've changed the one argument into a template parameter.):
#include <iostream>
int func0(bool b) { return b ? 1 : 2; }
//There is a few more funcX...
template<typename ...ARGS, int (*FUNC)(ARGS...)>
int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
int main()
{
std::cout << wrappedFunc0(true) << std::endl;
return 0;
}
This doesn't compile. It shows:
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
ASM generation compiler returned: 1
<source>: In function 'int wrappedFunc0(bool)':
<source>:9:55: error: no matching function for call to 'wrapper<bool, func0>(bool&)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:7:5: note: candidate: 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
7 | int wrapper(ARGS... args) { return (*FUNC)(args...) * 10; }
| ^~~~~~~
<source>:7:5: note: template argument deduction/substitution failed:
<source>:9:55: error: type/value mismatch at argument 1 in template parameter list for 'template<class ... ARGS, int (* FUNC)(ARGS ...)> int wrapper(ARGS ...)'
9 | int wrappedFunc0(bool b) { return wrapper<bool, func0>(b); }
| ~~~~~~~~~~~~~~~~~~~~^~~
<source>:9:55: note: expected a type, got 'func0'
Execution build compiler returned: 1
(link to the compiler explorer)
It looks like a problem with the compiler to me, but GCC and Clang agree on it so maybe it isn't.
Anyway, how can I make this template compile correctly with templated pointer to a function?
EDIT:
Addressing the duplicate flag Compilation issue with instantiating function template
I think the core of the problem in that question is the same as in mine, however, it lacks a solution that allows passing the pointer to function (not only its type) as a template parameter.
This doesn't work because a pack parameter (the one including ...) consumes all remaining arguments. All arguments following it can't be specified explicitly and must be deduced.
Normally you write such wrappers like this:
template <typename F, typename ...P>
int wrapper(F &&func, P &&... params)
{
return std::forward<F>(func)(std::forward<P>(params)...) * 10;
}
(And if the function is called more than once inside of the wrapper, all calls except the last can't use std::forward.)
This will pass the function by reference, which should be exactly the same as using a function pointer, but I have no reasons to believe that it would stop the compiler from optimizing it.
You can force the function to be encoded in the template argument by passing std::integral_constant<decltype(&func0), func0>{} instead of func0, but again, I don't think it's going to change anything.
The 2nd snippet is not valid because:
a type parameter pack cannot be expanded in its own parameter clause.
As from [temp.param]/17:
If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list.
So consider the following invalid example:
template<typename... Ts, Ts... vals> struct mytuple {}; //invalid
The above example is invalid because the template type parameter pack Ts cannot be expanded in its own parameter list.
For the same reason, your code example is invalid. For example, a simplified version of your 2nd snippet doesn't compile in msvc.
I'd like to send the two overloaded fun()s to one template that handles them plus their arguments. This is my attempt so far:
#include <vector>
#include <iostream>
using namespace std;
class Demo
{};
template<typename Function, typename... Args>
void call(Function func(Args...), Args &&...args)
{
func(forward<Args>(args)...); // execute function with args
}
void fun(int first, int second, int third)
{
cout << "fun with ints\n";
}
void fun(Demo &&dem1, Demo &&dem2) // adding overload causes the ambiguity
{
cout << "fun with Demos\n";
}
int main()
{
call(fun, 1, 2, 3);
call(fun, Demo{}, Demo{});
}
The compiler complains that it can't find a matching function for the calls in main():
main.cc: In function ‘int main()’:
main.cc:27:22: error: no matching function for call to ‘call(<unresolved overloaded function type>, int, int, int)’
27 | call(fun, 1, 2, 3);
| ^
main.cc:10:6: note: candidate: ‘template<class Function, class ... Args> void call(Function (*)(Args ...), Args&& ...)’
10 | void call(Function func(Args...), Args &&...args)
| ^~~~
main.cc:10:6: note: template argument deduction/substitution failed:
main.cc:27:22: note: couldn’t deduce template parameter ‘Function’
27 | call(fun, 1, 2, 3);
| ^
main.cc:29:29: error: no matching function for call to ‘call(<unresolved overloaded function type>, Demo, Demo)’
29 | call(fun, Demo{}, Demo{});
| ^
main.cc:10:6: note: candidate: ‘template<class Function, class ... Args> void call(Function (*)(Args ...), Args&& ...)’
10 | void call(Function func(Args...), Args &&...args)
| ^~~~
main.cc:10:6: note: template argument deduction/substitution failed:
main.cc:29:29: note: couldn’t deduce template parameter ‘Function’
29 | call(fun, Demo{}, Demo{});
|
Any ideas to find a solution to this puzzle would be highly appreciated!
(the solution at C++ overloaded function as template argument did not solve my problem because I cannot change the way that call() is called in main() )
The reason for compilation error is that the compiler does not know which fun overload you are actually going to use.
To resolve this error, you just need to cast your function parameter to the right overload like:
int main()
{
call( static_cast< void(*)(int, int, int) >( fun ), 1, 2, 3 );
call( static_cast< void(*)(Demo&&, Demo&&) >( fun ), Demo{}, Demo{} );
return 0;
}
FYI, what your call function is trying to do is actually defined by the standard. It is std::invoke function and it comes with C++17 standard.
the code is as follows
#include <iostream>
#include <functional>
using namespace std;
template<class F, class ...Args>
result_of_t<F> foo(F&& f,Args&&... args){
cout<<sizeof...(args);
f(args...);
}
int main(){
foo([](char a){ cout<<a<<'\n'; },'a');
return 0;
}
while I compile the code, it says
template.cpp:12:38: error: no matching function for call to ‘foo(main()::<lambda(char)>, char)’
the full compilation error is as follows
template.cpp: In function ‘int main()’:
template.cpp:12:38: error: no matching function for call to ‘foo(main()::<lambda(char)>, char)’
12 | foo([](char a){ cout<<a<<'\n'; },'a');
| ^
template.cpp:6:16: note: candidate: ‘template<class F, class ... Args> std::result_of_t<F> foo(F&&, Args&& ...)’
6 | result_of_t<F> foo(F&& f,Args&&... args){
| ^~~
template.cpp:6:16: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/10.2.0/bits/move.h:57,
from /usr/include/c++/10.2.0/bits/nested_exception.h:40,
from /usr/include/c++/10.2.0/exception:148,
from /usr/include/c++/10.2.0/ios:39,
from /usr/include/c++/10.2.0/ostream:38,
from /usr/include/c++/10.2.0/iostream:39,
from template.cpp:1:
/usr/include/c++/10.2.0/type_traits: In substitution of ‘template<class _Tp> using result_of_t = typename std::result_of::type [with _Tp = main()::<lambda(char)>]’:
template.cpp:6:16: required by substitution of ‘template<class F, class ... Args> std::result_of_t<F> foo(F&&, Args&& ...) [with F = main()::<lambda(char)>; Args = {char}]’
template.cpp:12:38: required from here
/usr/include/c++/10.2.0/type_traits:2570:11: error: invalid use of incomplete type ‘class std::result_of<main()::<lambda(char)> >’
2570 | using result_of_t = typename result_of<_Tp>::type;
| ^~~~~~~~~~~
/usr/include/c++/10.2.0/type_traits:2344:11: note: declaration of ‘class std::result_of<main()::<lambda(char)> >’
2344 | class result_of;
| ^~~~~~~~~
why the first statement of main function cannot match the function?
Because return type of foo cannot be deduced.
result_of takes full signature of functor, Args.. is missing there.
template<class F, class ...Args>
result_of_t< F(Args...) > foo(F&& f,Args&&... args){
cout<<sizeof...(args);
f(args...);
}
Demo
I wanted to implement a overload for operator<< that allowed me to call a given function and output the result.
I therefore wrote an overload, but the conversion to bool is selected and when writing a function myself, it would not compile.
EDIT: Know that I do not want to call the lambda,
but instead pass it to the function where it should be called with a default constructed parameter list.
I have appended my code:
#include <iostream>
template<typename T>
void test(T *) {
std::cout << "ptr" << std::endl;
}
template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
template<typename Ret, typename ...Args>
void test(Ret(*el)(Args...)) {
std::cout << "function ptr\n" << el(Args()...) << std::endl;
}
template<typename Char_T, typename Char_Traits, typename Ret, typename ...Args>
std::basic_ostream<Char_T, Char_Traits>& operator<<(
std::basic_ostream<Char_T, Char_Traits> &str, Ret(*el)(Args...)) {
return str << el(Args()...);
}
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test([]{return 5;}); // will not compile
}
I use gcc 7.3.1 with the version flag -std=c++14.
EDIT: Error message:
main.cc: In function ‘int main()’:
main.cc:25:23: error: no matching function for call to ‘test(main()::<lambda()>)’
test([]{return 5;});
^
main.cc:5:6: note: candidate: template<class T> void test(T*)
void test(T *) {
^~~~
main.cc:5:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘T*’ and ‘main()::<lambda()>’
test([]{return 5;});
^
main.cc:9:6: note: candidate: template<class T> void test(bool)
void test(bool) {
^~~~
main.cc:9:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: couldn't deduce template parameter ‘T’
test([]{return 5;});
^
main.cc:13:6: note: candidate: template<class Ret, class ... Args> void test(Ret (*)(Args ...))
void test(Ret(*el)(Args...)) {
^~~~
main.cc:13:6: note: template argument deduction/substitution failed:
main.cc:25:23: note: mismatched types ‘Ret (*)(Args ...)’ and ‘main()::<lambda()>’
test([]{return 5;});
Your problem here is that Template Argument Deduction is only done on the actual argument passed to test. It's not done on all possible types that the argument could possibly converted to. That might be an infinite set, so that's clearly a no-go.
So, Template Argument Deduction is done on the actual lambda object, which has an unspeakable class type. So the deduction for test(T*) fails as the lambda object is not a pointer. T can't be deduced from test(bool), obviously. Finally, the deduction fails for test(Ret(*el)(Args...)) as the lambda object is not a pointer-to-function either.
There are a few options. You might not even need a template, you could accept a std::function<void(void)> and rely on the fact that it has a templated constructor. Or you could just take a test(T t) argument and call it as t(). T will now deduce to the actual lambda type. The most fancy solution is probably using std::invoke, and accepting a template vararg list.
Even though non-capturing lambdas have an implicit conversion to function pointers, function templates must match exactly for deduction to succeed, no conversions will be performed.
Therefore the easiest fix is to force the conversion with a +
int main() {
std::boolalpha(std::cout);
std::cout << []{return 5;} << std::endl; // true is outputted
test(+[]{return 5;});
// ^
}
template<typename T>
void test(bool) {
std::cout << "bool" << std::endl;
}
Template is not needed. In fact you overload functions, not templates. Replace it with
void test(bool) {
std::cout << "bool" << std::endl;
}
Now your sample will compile.
How do I get boost::async to pass a function argument to the function it executes. My code is
#include <iostream>
// This is for the boos version
#include <boost/thread/future.hpp>
using boost::async;
// This is for the standard library version
//~ #include <future>
//~ using std::async;
int f(int x) {
std::cout << " in f " << x << std::endl;
return x+1;
}
int main() {
auto f_res = async(f,3);
std::cout << "f " << f_res.get() << std::endl;
return 0;
}
I compile with
g++ -std=c++14 -lboost_thread -lboost_system -lpthread test_async_boost.cc
, g++ version 5.3.0 and get many errors, complaining about the number of arguments async is willing to take:
test_async_boost_2.cc: In function 'int main()':
test_async_boost_2.cc:16:26: error: no matching function for call to 'async(int (&)(int), int)'
auto f_res = async(f,3);
^
In file included from test_async_boost_2.cc:3:0:
/usr/include/boost/thread/future.hpp:4035:3: note: candidate: template<class F> boost::unique_future<typename boost::result_of<F()>::type> boost::async(F&&)
async(BOOST_THREAD_FWD_REF(F) f) {
^
/usr/include/boost/thread/future.hpp:4035:3: note: template argument deduction/substitution failed:
test_async_boost_2.cc:16:26: note: candidate expects 1 argument, 2 provided
auto f_res = async(f,3);
^
In file included from test_async_boost_2.cc:3:0:
/usr/include/boost/thread/future.hpp:4018:3: note: candidate: template<class R> boost::unique_future<T> boost::async(R (*)())
async(R(*f)()) {
^
/usr/include/boost/thread/future.hpp:4018:3: note: template argument deduction/substitution failed:
test_async_boost_2.cc:16:26: note: candidate expects 0 arguments, 1 provided
auto f_res = async(f,3);
^
In file included from test_async_boost_2.cc:3:0:
/usr/include/boost/thread/future.hpp:3695:3: note: candidate: template<class F> boost::unique_future<typename boost::result_of<typename boost::decay<T>::type()>::type> boost::async(boost::launch, F&&)
async(launch policy, BOOST_THREAD_FWD_REF(F) f) {
^
/usr/include/boost/thread/future.hpp:3695:3: note: template argument deduction/substitution failed:
test_async_boost_2.cc:16:26: note: cannot convert 'f' (type 'int(int)') to type 'boost::launch'
auto f_res = async(f,3);
^
In file included from test_async_boost_2.cc:3:0:
/usr/include/boost/thread/future.hpp:3634:3: note: candidate: template<class R> boost::unique_future<T> boost::async(boost::launch, R (*)())
async(launch policy, R(*f)()) {
^
/usr/include/boost/thread/future.hpp:3634:3: note: template argument deduction/substitution failed:
test_async_boost_2.cc:16:26: note: cannot convert 'f' (type 'int(int)') to type 'boost::launch'
auto f_res = async(f,3);
^
If I switch the commenting in the #include directuves and the using lines, and compile with
g++ -std=c++14 test_async_boost_2.cc -lpthread
I get the desired output:
in f 3
f 4
How do I get boost::async to work with function arguments?
And: Where do I find the reference documentation of boos::async?
In order to pass a function with its arguments to the method accepting a function, you have to use std::bind or boost::bind.