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.
Since you're starting the thread with no arguments, you expect the task to be started with no arguments, as if task1()
were used. Hence the signature that you want to support is not int(int, int)
but int()
. In turn, this means that you must pass a functor that is compatible with this signature to the constructor of std::packaged_task<int()>
. Try:
packaged_task<int()> task1 { std::bind(&ackermann, 3, 11) };
Another possibility is:
packaged_task<int(int,int)> task1 { &ackermann };
auto f1 = task1.get_future();
thread th1 { move(task1), 3, 11 };
because the constructor of std::thread
can accept arguments. Here, the functor you pass to it will be used as if task1(3, 11)
were used.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With