I'm attempting to make a threadpool that takes any kind of function, and returns a future for any return value/exceptions that function may have. I'm doing this largely as an exercise to learn modern threading and some template programming. I tried basing my syntax loosely off how MSVC does std::function and futures.
Here's a minimum snippet of where my problem is:
#include <functional>
#include <future>
#include <utility>
#include <queue>
#include <memory>
using Job = std::function<void()>;
std::queue<std::unique_ptr<Job>> queue;
template<typename FuncType, typename... Args>
auto add(FuncType&& func, Args&&... args) ->std::future<decltype(func)(decltype(args)...)> {
auto task = std::packaged_task<decltype(func)(decltype(args)...)>(std::bind (std::forward<FuncType>(func), std::forward<Args>(args)...));
auto future = task.get_future();
queue.push(std::make_unique<Job>([task]() { task(); }));
return future;
}
void voidFunc(){};
int main()
{
add(voidFunc);
}
This fails to compile with the errors:
/usr/include/c++/4.9/future: In instantiation of 'class std::future<void (&())()>':
28:17: required from here
/usr/include/c++/4.9/future:697:7: error: function returning a function
get()
^
In instantiation of 'add(FuncType&&, Args&& ...)::<lambda()> [with FuncType = void (&)(); Args = {}]':
19:37: required from 'struct add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]::<lambda()>'
19:56: required from 'std::future<decltype (func)(decltype (args)...)> add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]'
28:17: required from here
19:52: error: passing 'const std::packaged_task<void (&())()>' as 'this' argument of 'void std::packaged_task<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) [with _Res = void (&)(); _ArgTypes = {}]' discards qualifiers [-fpermissive]
In instantiation of 'std::future<decltype (func)(decltype (args)...)> add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]':
28:17: required from here
19:36: error: use of deleted function 'std::packaged_task<_Res(_ArgTypes ...)>::packaged_task(const std::packaged_task<_Res(_ArgTypes ...)>&) [with _Res = void (&)(); _ArgTypes = {}]'
In file included from 5:0:
/usr/include/c++/4.9/future:1413:7: note: declared here
packaged_task(const packaged_task&) = delete;
^
21:10: error: could not convert 'future' from 'std::future<void (&)()>' to 'std::future<void (&())()>'
In file included from 5:0:
/usr/include/c++/4.9/future: In instantiation of 'static std::__future_base::_Task_setter<_Res_ptr> std::__future_base::_S_task_setter(_Res_ptr&, _BoundFn&&) [with _Res_ptr = std::unique_ptr<std::__future_base::_Result<void (&)()>, std::__future_base::_Result_base::_Deleter>; _BoundFn = std::_Bind_simple<std::reference_wrapper<std::_Bind<void (*())()> >()>; typename _Res_ptr::element_type::result_type = void (&)()]':
/usr/include/c++/4.9/future:1318:70: required from 'void std::__future_base::_Task_state<_Fn, _Alloc, _Res(_Args ...)>::_M_run(_Args ...) [with _Fn = std::_Bind<void (*())()>; _Alloc = std::allocator<int>; _Res = void (&)(); _Args = {}]'
29:1: required from here
/usr/include/c++/4.9/future:539:57: error: could not convert 'std::ref(_Tp&) [with _Tp = std::_Bind_simple<std::reference_wrapper<std::_Bind<void (*())()> >()>]()' from 'std::reference_wrapper<std::_Bind_simple<std::reference_wrapper<std::_Bind<void (*())()> >()> >' to 'std::function<void (&())()>'
return _Task_setter<_Res_ptr>{ __ptr, std::ref(__call) };
^
In file included from /usr/include/c++/4.9/memory:81:0,
from /usr/include/c++/4.9/thread:40,
from /usr/include/c++/4.9/future:40,
from 5:
/usr/include/c++/4.9/bits/unique_ptr.h:764:5: error: 'typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = std::function<void()>; _Args = {add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]::<lambda()>}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<std::function<void()> >]', declared using local type 'add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]::<lambda()>', is used but never defined [-fpermissive]
make_unique(_Args&&... __args)
^
In function 'std::future<decltype (func)(decltype (args)...)> add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]':
22:2: warning: control reaches end of non-void function [-Wreturn-type]
I think I have two problems here: I don't know how to properly use decltype to get the right function signature (I've also looked into invoke_result, but I wasn't having any luck there either), and I think I'm probably also not passing the packaged task to the queue properly.
How do I get the right function signature for the future and packaged task, and how do I properly pass the packaged task to a std::function on a queue (that will later be grabbed by another thread)?
Two problems:
1) The signature of packaged task should be
std::packaged_task<std::invoke_result_t<Func&&,Args&&...>(Args&&...)>. This uses invoke_result_t to compute the return type, but also passes the argument types to the packaged task.
2) Bigger problem: std::function requires that the function be copy constructable, which std::packaged_task is not. You'll have to create your own queue to hold the packaged tasks. I've implemented it before using a base class Task with a templated derived class to hold the packaged task.
Related
Here's a watered down example of the code I'm try to compile:
#include <iostream>
#include <functional>
template <class F>
auto foo(F&& fun)
{
return [callback = std::forward<F>(fun)](auto&&... args) {
std::invoke(callback, std::forward<decltype(args)>(args)...);
};
}
int main()
{
std::string cur("running"), target("ok");
foo([s1 = cur, s2 = target](std::string const& arg) /*mutable*/ {
if (s1 == arg)
{
std::cout << s1 << std::endl;
}
})("not ok");
return 0;
}
put simply I have a function foo that accepts callables, and is supposed to build a new callable from them. For the sake of the example, above I'm just invoking the fun argument but in the real case there are some decorations done to the callable and the result is placed into a data structure that invokes such "actions" under some conditions.
This example compiles and works just fine. The problem manifests when trying to pass mutable lambdas to foo. When I uncomment the mutable keyword above, I get this compilation error:
main.cpp: In instantiation of 'foo<main()::<lambda(const string&)> >(main()::<lambda(const string&)>&&)::<lambda(auto:1&& ...)> [with auto:1 = {const char (&)[7]}]':
main.cpp:21:7: required from here
main.cpp:8:20: error: no matching function for call to 'invoke(const main()::<lambda(const string&)>&, const char [7])'
8 | std::invoke(callback, std::forward<decltype(args)>(args)...);
| ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from main.cpp:2:
/usr/local/include/c++/11.2.0/functional:94:5: note: candidate: 'template<class _Callable, class ... _Args> std::invoke_result_t<_Callable, _Args ...> std::invoke(_Callable&&, _Args&& ...)'
94 | invoke(_Callable&& __fn, _Args&&... __args)
| ^~~~~~
/usr/local/include/c++/11.2.0/functional:94:5: note: template argument deduction/substitution failed:
In file included from /usr/local/include/c++/11.2.0/bits/move.h:57,
from /usr/local/include/c++/11.2.0/bits/nested_exception.h:40,
from /usr/local/include/c++/11.2.0/exception:148,
from /usr/local/include/c++/11.2.0/ios:39,
from /usr/local/include/c++/11.2.0/ostream:38,
from /usr/local/include/c++/11.2.0/iostream:39,
from main.cpp:1:
/usr/local/include/c++/11.2.0/type_traits: In substitution of 'template<class _Fn, class ... _Args> using invoke_result_t = typename std::invoke_result::type [with _Fn = const main()::<lambda(const string&)>&; _Args = {const char (&)[7]}]':
/usr/local/include/c++/11.2.0/functional:94:5: required by substitution of 'template<class _Callable, class ... _Args> std::invoke_result_t<_Callable, _Args ...> std::invoke(_Callable&&, _Args&& ...) [with _Callable = const main()::<lambda(const string&)>&; _Args = {const char (&)[7]}]'
main.cpp:8:20: required from 'foo<main()::<lambda(const string&)> >(main()::<lambda(const string&)>&&)::<lambda(auto:1&& ...)> [with auto:1 = {const char (&)[7]}]'
main.cpp:21:7: required from here
/usr/local/include/c++/11.2.0/type_traits:2933:11: error: no type named 'type' in 'struct std::invoke_result<const main()::<lambda(const string&)>&, const char (&)[7]>'
2933 | using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
| ^~~~~~~~~~~~~~~
Any idea why is that? Can my foo accept mutable lambdas as well?
Just add mutable to the lambda inside the foo:
template <class F>
auto foo(F&& fun)
{
return [callback = std::forward<F>(fun)](auto&&... args) mutable {
//^^^
std::invoke(callback, std::forward<decltype(args)>(args)...);
};
}
This is my (simplified) code where I'm trying to call async forwarding arguments:
template<typename Ret, typename ... Args>
class CallbackAsyncTask {
public:
CallbackAsyncTask() {
}
virtual ~CallbackAsyncTask() = default;
void execute( Args&& ... args ) {
execute(&CallbackAsyncTask<Ret, Args...>::onBackground, this, std::forward<Args>(args)...);
}
protected:
virtual Ret onBackground( Args& ... args ) = 0;
template<typename Fn, typename ... Argss>
void execute( Fn&& fn, Argss&& ... args ) noexcept(false) {
std::async(std::launch::async, std::forward<Fn>(fn), std::forward<Argss>(args)...);
}
};
class Child: public CallbackAsyncTask<int, int> {
public:
virtual int onBackground( int& i ) {
return i;
}
};
int main() {
Child c;
c.execute(15);
return 0;
}
I get this error:
../main.cpp: In instantiation of ‘void CallbackAsyncTask<Ret, Args>::execute(Fn&&, Argss&& ...) [with Fn = int (CallbackAsyncTask<int, int>::*)(int&); Argss = {CallbackAsyncTask<int, int>* const, int}; Ret = int; Args = {int}]’:
../main.cpp:27:98: required from ‘void CallbackAsyncTask<Ret, Args>::execute(Args&& ...) [with Ret = int; Args = {int}]’
../main.cpp:46:17: required from here
../main.cpp:33:90: error: no matching function for call to ‘async(std::launch, int (CallbackAsyncTask<int, int>::*)(int&), CallbackAsyncTask<int, int>* const, int)’
std::async(std::launch::async, std::forward<Fn>(fn), std::forward<Argss>(args)...);
^
../main.cpp:33:90: note: candidates are:
In file included from ../main.cpp:13:0:
/usr/include/c++/4.8.2/future:1532:5: note: template<class _Fn, class ... _Args> std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...)
async(launch __policy, _Fn&& __fn, _Args&&... __args)
^
/usr/include/c++/4.8.2/future:1532:5: note: template argument deduction/substitution failed:
/usr/include/c++/4.8.2/future: In substitution of ‘template<class _Fn, class ... _Args> std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = int (CallbackAsyncTask<int, int>::*)(int&); _Args = {CallbackAsyncTask<int, int>* const, int}]’:
../main.cpp:33:90: required from ‘void CallbackAsyncTask<Ret, Args>::execute(Fn&&, Argss&& ...) [with Fn = int (CallbackAsyncTask<int, int>::*)(int&); Argss = {CallbackAsyncTask<int, int>* const, int}; Ret = int; Args = {int}]’
../main.cpp:27:98: required from ‘void CallbackAsyncTask<Ret, Args>::execute(Args&& ...) [with Ret = int; Args = {int}]’
../main.cpp:46:17: required from here
/usr/include/c++/4.8.2/future:1532:5: error: no type named ‘type’ in ‘class std::result_of<int (CallbackAsyncTask<int, int>::*(CallbackAsyncTask<int, int>*, int))(int&)>’
../main.cpp: In instantiation of ‘void CallbackAsyncTask<Ret, Args>::execute(Fn&&, Argss&& ...) [with Fn = int (CallbackAsyncTask<int, int>::*)(int&); Argss = {CallbackAsyncTask<int, int>* const, int}; Ret = int; Args = {int}]’:
../main.cpp:27:98: required from ‘void CallbackAsyncTask<Ret, Args>::execute(Args&& ...) [with Ret = int; Args = {int}]’
../main.cpp:46:17: required from here
/usr/include/c++/4.8.2/future:1552:5: note: template<class _Fn, class ... _Args> std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(_Fn&&, _Args&& ...)
async(_Fn&& __fn, _Args&&... __args)
^
/usr/include/c++/4.8.2/future:1552:5: note: template argument deduction/substitution failed:
/usr/include/c++/4.8.2/future: In substitution of ‘template<class _Fn, class ... _Args> std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(_Fn&&, _Args&& ...) [with _Fn = std::launch; _Args = {int (CallbackAsyncTask<int, int>::*)(int&), CallbackAsyncTask<int, int>* const, int}]’:
../main.cpp:33:90: required from ‘void CallbackAsyncTask<Ret, Args>::execute(Fn&&, Argss&& ...) [with Fn = int (CallbackAsyncTask<int, int>::*)(int&); Argss = {CallbackAsyncTask<int, int>* const, int}; Ret = int; Args = {int}]’
../main.cpp:27:98: required from ‘void CallbackAsyncTask<Ret, Args>::execute(Args&& ...) [with Ret = int; Args = {int}]’
../main.cpp:46:17: required from here
/usr/include/c++/4.8.2/future:1552:5: error: no type named ‘type’ in ‘class std::result_of<std::launch(int (CallbackAsyncTask<int, int>::*)(int&), CallbackAsyncTask<int, int>*, int)>’
What I am missing? Compiler Gcc 4.8.3
The root cause is these two lines
virtual Ret onBackground( Args& ... args ) = 0;
virtual int onBackground( int& i )
When launching the task, the arguments are converted either directly or as if by the std::thread constructor. That one does decay_copy on the arguments when invoking the callable, so a non-const lvalue reference cannot bind to the argument.
Change the way onBackground accepts arguments. For instance, a const lvalue refernce works. See live.
Im trying to create a simple wrapper to be used by my application when creating threads in lew of std::thread.
The only purpose of using this app thread wrapper is to ensure some code gets invoked on every spawned thread consistently. I thought this would be trivial, but the std::thread constructor and argument passing is rather complex and Im getting very cryptic build errors here.
This is a simple example of what I'm trying to do:
#include <thread>
class AppThread : public std::thread
{
template< class Function, class... Args >
static void wrap( Function&& f, Args&&... args )
{
//Some code
f( std::forward<Args>( args )... );
}
public:
template< class Function, class... Args >
explicit AppThread( Function&& f, Args&&... args ) : std::thread( AppThread::wrap<Function,Args...>,
std::forward<Function>( f ), std::forward<Args>( args )... )
{}
};
void runA() {}
void runB( int x ) {}
main()
{
AppThread thread1 = AppThread( runA );
//AppThread thread2 = AppThread( runB, 5 );
}
I'd like to be able to drop in AppThread wherever std:thread is being used, so extending and overriding the constructor seems like the best approach. But passing those arguments through to my wrapped method causes this cascade of errors ( gcc 7.2 )
In file included from thread_wrap.cpp:1:0:
gcc-7.2.0/include/c++/7.2.0/thread: In instantiation of ‘struct std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >’:
gcc-7.2.0/include/c++/7.2.0/thread:127:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void (&)()); _Args = {void (&)()}]’
thread_wrap.cpp:15:130: required from ‘AppThread::AppThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]’
thread_wrap.cpp:24:41: required from here
gcc-7.2.0/include/c++/7.2.0/thread:240:2: error: no matching function for call to ‘std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_M_invoke(std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_Indices)’
operator()()
^~~~~~~~
gcc-7.2.0/include/c++/7.2.0/thread:231:4: note: candidate: template<long unsigned int ..._Ind> decltype (std::__invoke((_S_declval<_Ind>)()...)) std::thread::_Invoker<_Tuple>::_M_invoke(std::_Index_tuple<_Ind ...>) [with long unsigned int ..._Ind = {_Ind ...}; _Tuple = std::tuple<void (*)(void (&)()), void (*)()>]
_M_invoke(_Index_tuple<_Ind...>)
^~~~~~~~~
gcc-7.2.0/include/c++/7.2.0/thread:231:4: note: template argument deduction/substitution failed:
gcc-7.2.0/include/c++/7.2.0/thread: In substitution of ‘template<long unsigned int ..._Ind> decltype (std::__invoke(_S_declval<_Ind>()...)) std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_M_invoke<_Ind ...>(std::_Index_tuple<_Ind1 ...>) [with long unsigned int ..._Ind = {0, 1}]’:
gcc-7.2.0/include/c++/7.2.0/thread:240:2: required from ‘struct std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >’
gcc-7.2.0/include/c++/7.2.0/thread:127:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void (&)()); _Args = {void (&)()}]’
thread_wrap.cpp:15:130: required from ‘AppThread::AppThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]’
thread_wrap.cpp:24:41: required from here
gcc-7.2.0/include/c++/7.2.0/thread:233:29: error: no matching function for call to ‘__invoke(std::__tuple_element_t<0, std::tuple<void (*)(void (&)()), void (*)()> >, std::__tuple_element_t<1, std::tuple<void (*)(void (&)()), void (*)()> >)’
-> decltype(std::__invoke(_S_declval<_Ind>()...))
~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
In file included from gcc-7.2.0/include/c++/7.2.0/tuple:41:0,
from gcc-7.2.0/include/c++/7.2.0/bits/unique_ptr.h:37,
from gcc-7.2.0/include/c++/7.2.0/memory:80,
from gcc-7.2.0/include/c++/7.2.0/thread:39,
from thread_wrap.cpp:1:
gcc-7.2.0/include/c++/7.2.0/bits/invoke.h:89:5: note: candidate: template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)
__invoke(_Callable&& __fn, _Args&&... __args)
^~~~~~~~
gcc-7.2.0/include/c++/7.2.0/bits/invoke.h:89:5: note: template argument deduction/substitution failed:
gcc-7.2.0/include/c++/7.2.0/bits/invoke.h: In substitution of ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = void (*)(void (&)()); _Args = {void (*)()}]’:
gcc-7.2.0/include/c++/7.2.0/thread:233:29: required by substitution of ‘template<long unsigned int ..._Ind> decltype (std::__invoke(_S_declval<_Ind>()...)) std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >::_M_invoke<_Ind ...>(std::_Index_tuple<_Ind1 ...>) [with long unsigned int ..._Ind = {0, 1}]’
gcc-7.2.0/include/c++/7.2.0/thread:240:2: required from ‘struct std::thread::_Invoker<std::tuple<void (*)(void (&)()), void (*)()> >’
gcc-7.2.0/include/c++/7.2.0/thread:127:22: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void (&)()); _Args = {void (&)()}]’
thread_wrap.cpp:15:130: required from ‘AppThread::AppThread(Function&&, Args&& ...) [with Function = void (&)(); Args = {}]’
thread_wrap.cpp:24:41: required from here
gcc-7.2.0/include/c++/7.2.0/bits/invoke.h:89:5: error: no type named ‘type’ in ‘struct std::__invoke_result<void (*)(void (&)()), void (*)()>’
Could definitely use some insight here!
Thanks
Here's an example of what I think Sam is driving at. As you can see, it is only a few lines of code so I'll leave it at that.
#include <iostream>
#include <thread>
#include <functional>
void preamble (void) { std::cout << "preamble\n"; }
template <class F, class ... Args> std::thread ThreadWrapper (F f, Args&& ... args)
{
return std::thread ([f, args...] () { preamble (); f (std::forward <Args...> (args...)); });
};
int main()
{
std::thread t = ThreadWrapper ([] (std::string s) { std::cout << s << "\n"; }, "42");
t.join ();
}
Output:
preamble
42
Live demo
I figured this out when I woke up this morning :)
Since std::thread decays all arguments it passes to the function, by the time it calls my wrap method the arguments are the decayed types. However, when I'm passing the template args to wrap<> it gets the undecayed types:
std::thread( AppThread::wrap<Function,Args...>,
Solution is simple, I need decay the types before instantiation the template wrap method:
std::thread( AppThread::wrap<std::decay_t<Function>,std::decay_t<Args>...>,
Consider the following set of examples.
The function takeOnlyVoidFunction takes a function with zero arguments and simply executes it.
The function takeVariableArguments takes a variable number of arguments and executes the function using the arguments.
The function captureVariableArgs attempts to convert the second function into a lambda form that is acceptable by the first function, but it does not compile.
How can I make the function captureVariableArgs compile and exhibit the correct behavior of converting a function with a variable number of arguments into a closure with no arguments?
#include <stdio.h>
#include <functional>
void takeOnlyVoidFunction(std::function<void()> task) {
task();
}
template<typename _Callable, typename... _Args>
void takeVariableArguments(_Callable&& __f, _Args&&... __args) {
__f(__args...);
}
// How can I make this function compile?
template<typename _Callable, typename... _Args>
void captureVariableArgs(_Callable&& __f, _Args&&... __args) {
takeOnlyVoidFunction([=]() { __f(__args...);});
}
void normalFunction(int a, int b) {
printf("I am a normal function which takes params (%d,%d)\n", a, b);
}
int main() {
int a = 7;
int b = 8;
takeVariableArguments(normalFunction, a, b);
takeOnlyVoidFunction([=](){ normalFunction(a,b);});
captureVariableArgs(normalFunction, a, b);
}
I'm running gcc 4.9.2. Here is the compiler error I see.
g++ -std=c++11 Test.cc -o Test
Test.cc: In instantiation of ‘captureVariableArgs(_Callable&&, _Args&& ...)::<lambda()> [with _Callable = void (&)(int, int); _Args = {int&, int&}]’:
Test.cc:16:38: required from ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’
Test.cc:16:50: required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’
Test.cc:28:45: required from here
Test.cc:16:34: error: variable ‘__f’ has function type
takeOnlyVoidFunction([=]() { __f(__args...);});
^
Test.cc:16:34: error: variable ‘__f’ has function type
Test.cc: In instantiation of ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’:
Test.cc:16:50: required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’
Test.cc:28:45: required from here
Test.cc:16:34: error: field ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>::<__f capture>’ invalidly declared function type
In file included from Test.cc:2:0:
/usr/include/c++/4.9/functional:2418:7: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>; <template-parameter-2-2> = void; _Res = void; _ArgTypes = {}]’, declared using local type ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’, is used but never defined [-fpermissive]
function<_Res(_ArgTypes...)>::
^
Update: A more minimal example demonstrating this problem.
#include <stdio.h>
// How can I make this function compile?
template<typename _Callable>
void captureVariableArgs(_Callable&& __f) {
takeOnlyVoidFunction( [=]{ __f(); } );
}
void normalFunction() {
printf("I am a normal function\n");
}
int main(){
captureVariableArgs(normalFunction);
}
As another potential workaround for GCC, instead of using a lambda, you could use std::bind:
template <typename F, typename... Args>
auto captureVariable(F&& f, Args&&... args)
{
return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}
This works for me under GCC 4.9.3.
The code in the post compiles fine with the latest clang&MSVC compilers but all the gccs refuse to compile it. So it seems a bug in gcc. Nevertheless, I found a way to make gcc happy: just don't use an "universal reference" on the callable argument, like this:
template<typename _Callable, typename... _Args>
int captureVariableArgs(_Callable _f, _Args&&... _args) {
return takeOnlyVoidFunction([=]() { _f(_args...);});
}
I can't explain why gcc doesn't accept your version, though. I'm not familiar with gcc-style error reporting and can't extract the true cause from the error message. But I think the workaround is ok since I don't see any value in "universal reference" in this case. In fact, I don't see why you use it on the args either.
I have this minimal not-working example of code
#include <future>
int main()
{
auto intTask = std::packaged_task<int()>( []()->int{ return 5; } );
std::packaged_task<void()> voidTask{ std::move(intTask) };
}
Why doesn't it compile (on gcc 4.8.1)? I suspect, the reason is, that std::packaged_task stores the lambda internally inside an std::function which needs a CopyConstructible argument. However, std::packaged_task is move-only. Is this a bug? What does the standard say about it? In my opinion std::packaged_task should not need a CopyConstructible argument, but a MoveConstructible argument should be enough.
By the way, when I replace std::packaged_task<int()> by std::packaged_task<void()> everything compiles fine.
GCC 4.8.1 is giving me this error message:
In file included from /usr/include/c++/4.6/future:38:0,
from ../cpp11test/main.cpp:160:
/usr/include/c++/4.6/functional: In static member function 'static void std::_Function_base::_Base_manager<_Functor>::_M_clone(std::_Any_data&, const std::_Any_data&, std::false_type) [with _Functor = std::packaged_task<int()>, std::false_type = std::integral_constant<bool, false>]':
/usr/include/c++/4.6/functional:1652:8: instantiated from 'static bool std::_Function_base::_Base_manager<_Functor>::_M_manager(std::_Any_data&, const std::_Any_data&, std::_Manager_operation) [with _Functor = std::packaged_task<int()>]'
/usr/include/c++/4.6/functional:2149:6: instantiated from 'std::function<_Res(_ArgTypes ...)>::function(_Functor, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type) [with _Functor = std::packaged_task<int()>, _Res = void, _ArgTypes = {}, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type = std::function<void()>::_Useless]'
/usr/include/c++/4.6/bits/shared_ptr_base.h:410:4: instantiated from 'std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {std::packaged_task<int()>}, _Tp = std::__future_base::_Task_state<void()>, _Alloc = std::allocator<std::__future_base::_Task_state<void()> >, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]'
/usr/include/c++/4.6/bits/shared_ptr_base.h:518:8: instantiated from 'std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = std::__future_base::_Task_state<void()>, _Alloc = std::allocator<std::__future_base::_Task_state<void()> >, _Args = {std::packaged_task<int()>}, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]'
/usr/include/c++/4.6/bits/shared_ptr_base.h:987:35: instantiated from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<std::__future_base::_Task_state<void()> >, _Args = {std::packaged_task<int()>}, _Tp = std::__future_base::_Task_state<void()>, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]'
/usr/include/c++/4.6/bits/shared_ptr.h:317:64: instantiated from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<std::__future_base::_Task_state<void()> >, _Args = {std::packaged_task<int()>}, _Tp = std::__future_base::_Task_state<void()>]'
/usr/include/c++/4.6/bits/shared_ptr.h:535:39: instantiated from 'std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = std::__future_base::_Task_state<void()>, _Alloc = std::allocator<std::__future_base::_Task_state<void()> >, _Args = {std::packaged_task<int()>}]'
/usr/include/c++/4.6/bits/shared_ptr.h:551:42: instantiated from 'std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = std::__future_base::_Task_state<void()>, _Args = {std::packaged_task<int()>}]'
/usr/include/c++/4.6/future:1223:66: instantiated from 'std::packaged_task<_Res(_ArgTypes ...)>::packaged_task(_Fn&&) [with _Fn = std::packaged_task<int()>, _Res = void, _ArgTypes = {}]'
../cpp11test/main.cpp:165:61: instantiated from here
/usr/include/c++/4.6/functional:1616:4: error: use of deleted function 'std::packaged_task<_Res(_ArgTypes ...)>::packaged_task(std::packaged_task<_Res(_ArgTypes ...)>&) [with _Res = int, _ArgTypes = {}, std::packaged_task<_Res(_ArgTypes ...)> = std::packaged_task<int()>]'
/usr/include/c++/4.6/future:1244:7: error: declared here
UPDATE: I have written the following test program. It seems to support the assumption that the reason is missing CopyConstructability. Again, what are the requirements on the type of the object from which an std::packaged_task may be constructed?
#include <future>
struct Functor {
Functor() {}
Functor( const Functor & ) {} // without this line it doesn't compile
Functor( Functor && ) {}
int operator()(){ return 5; }
};
int main() {
auto intTask = std::packaged_task<int()>( Functor{} );
}
Indeed, packaged_task only has a moving constructor (30.6.9/2):
template <class F> explicit packaged_task(F&& f);
However, your problem is the explicit constructor. So write it like this:
std::packaged_task<int()> pt([]() -> int { return 1; });
Complete example:
#include <future>
#include <thread>
int main()
{
std::packaged_task<int()> intTask([]() -> int { return 5; } );
auto f = intTask.get_future();
std::thread(std::move(intTask)).detach();
return f.get();
}
No. You just can't move a packaged_task<int ()> into a packaged_task<void ()>. These types a unrelated and cannot be move-assigned or move-constructed from each other. If you for some reason really want to do that you can "swallow" the result of the int () like this
The standard (as of N3690) doesn't state anything explicitly about the requirements of the type F in
template <class R, class... ArgTypes>
template <class F>
packaged_task<R(ArgTypes...)>::packaged_task(F&& f);
(see 30.6.9.1) However, it states that
Invoking a copy of f shall behave the same as invoking f.
and that this call can throw
any exceptions thrown by the copy or move constructor of f, or std::bad_alloc if memory
for the internal data structures could not be allocated.
This implicitly implies that the type F must be at least MoveConstructible, or CopyConstructible, if an lvalue reference is handed to the function.
Hence, it's not a bug, it's just not specified that precisely. To solve the problem of putting a std::packaged_task<int()> into a std::packaged_task<void()> just wrap the first into a shared_ptr like this:
#include <future>
#include <memory>
int main()
{
auto intTask = std::make_shared<std::packaged_task<int()>>(
[]()->int{ return 5; } );
std::packaged_task<void()> voidTask{ [=]{ (*intTask)(); } };
}