I've been looking for a workaround to the problem of type-erasing a std::packaged_task using std::function.
What I wanted to do was something like this:
#include <future>
#include <functional>
#include <iostream>
namespace {
std::function<void()> task;
std::future<int> make(int val) {
auto test = std::packaged_task<int()>([val](){
return val;
});
auto fut = test.get_future();
task = std::move(test);
return fut;
}
}
int main() {
auto fut = make(100);
task();
std::cout << fut.get() << "\n";
}
it's succinct and avoids re-implementing a lot of mechanics myself. Unfortunately that isn't actually legal because std::packaged_task is move-only not copy constructable.
As a workaround I came up with the following, which implements things in terms of std::promise and a std::shared_ptr instead:
#include <future>
#include <functional>
#include <iostream>
namespace {
std::function<void()> task;
std::future<int> make(int val) {
auto test = std::make_shared<std::promise<int>>();
task = [test,val]() {
test->set_value(val);
test.reset(); // This is important
};
return test->get_future();
}
}
int main() {
auto fut = make(100);
task();
std::cout << fut.get() << "\n";
}
This "works for me", but is this actually correct code? Is there a nicer way to achieve the same net result?
(Note that the lifespan of the std::shared_ptr in the second example is important for my real code. Clearly as-is I will be taking steps to prevent calling the same std::function twice).
Your problem seems to stem from type incompatabilities:
std::function<void()> task; // Notice this is not a package_task
So why would you expect this to work?
task = std::move(test); // when test is `std::packaged_task<int()>`
When I change task to the correct type it compiles as expected:
namespace {
std::packaged_task<int()> task; // Change this.
std::future<int> make(int val) {
auto test = std::packaged_task<int()>([val](){
return val;
});
auto fut = test.get_future();
task = std::move(test); // This now compiles.
return fut;
}
}
Personally since type is important I would remove the auto from test.
namespace {
std::packaged_task<int()> task;
std::future<int> make(int val) {
std::packaged_task<int()> test([val](){return val;});
auto fut = test.get_future();
task = std::move(test);
return fut;
}
}
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