Following this excellent tutorial for futures, promises and packaged tasks I got to the the point where I wanted to prepare my own task
#include <iostream>
#include <future>
using namespace std;
int ackermann(int m, int n) { // might take a while
if(m==0) return n+1;
if(n==0) return ackermann(m-1,1);
return ackermann(m-1, ackermann(m, n-1));
}
int main () {
packaged_task<int(int,int)> task1 { &ackermann, 3, 11 }; // <- error
auto f1 = task1.get_future();
thread th1 { move(task1) }; // call
cout << " ack(3,11):" << f1.get() << endl;
th1.join();
}
As far as I can decipher the gcc-4.7.0 error message it expects the arguments differently? But how? I try to shorten the error message:
error: no matching function for call to
'std::packaged_task<int(int, int)>::packaged_task(<brace-enclosed initializer list>)'
note: candidates are:
std::packaged_task<_Res(_ArgTypes ...)>::---<_Res(_ArgTypes ...)>&&) ---
note: candidate expects 1 argument, 3 provided
...
note: cannot convert 'ackermann'
(type 'int (*)(int, int)') to type 'std::allocator_arg_t'
Is my variant how I provide the parameters for ackermann
wrong? Or is it the wrong template parameter? I do not give the parameters 3,11
to the creation of thread, right?
Update other unsuccessful variants:
packaged_task<int()> task1 ( []{return ackermann(3,11);} );
thread th1 { move(task1) };
packaged_task<int()> task1 ( bind(&ackermann,3,11) );
thread th1 { move(task1) };
packaged_task<int(int,int)> task1 ( &ackermann );
thread th1 { move(task1), 3,11 };
hmm... is it me, or is it the beta-gcc?
Firstly, if you declare std::packaged_task
to take arguments, then you must pass them to operator()
, not the constructor. In a single thread you can thus do:
std::packaged_task<int(int,int)> task(&ackermann);
auto f=task.get_future();
task(3,11);
std::cout<<f.get()<<std::endl;
To do the same with a thread, you must move the task into the thread, and pass the arguments too:
std::packaged_task<int(int,int)> task(&ackermann);
auto f=task.get_future();
std::thread t(std::move(task),3,11);
t.join();
std::cout<<f.get()<<std::endl;
Alternatively, you can bind the arguments directly before you construct the task, in which case the task itself now has a signature that takes no arguments:
std::packaged_task<int()> task(std::bind(&ackermann,3,11));
auto f=task.get_future();
task();
std::cout<<f.get()<<std::endl;
Again, you can do this and pass it to a thread:
std::packaged_task<int()> task(std::bind(&ackermann,3,11));
auto f=task.get_future();
std::thread t(std::move(task));
t.join();
std::cout<<f.get()<<std::endl;
All of these examples should work (and do, with both g++ 4.6 and MSVC2010 and my just::thread implementation of the thread library). If any do not then there is a bug in the compiler or library you are using. For example, the library shipped with g++ 4.6 cannot handle passing move-only objects such as a std::packaged_task
to std::thread
(and thus fails to handle the 2nd and 4th examples), since it uses std::bind
as an implementation detail, and that implementation of std::bind
incorrectly requires that the arguments are copyable.