In the following sample code I want to create an Item
object from a Component
:
struct Component { };
struct Item {
explicit Item(Component component) : comp(component) {}
Component comp;
};
struct Factory {
static std::future<Item> get_item() {
std::future<Component> component = get_component();
// how to get a std::future<Item> ?
}
std::future<Component> get_component();
};
How do I go from std::future<Component>
to std::future<Item>
?
Update: removed my first idea (which was thread-based) from the question and posted an answer.
Needs moar packaged_tasks!
std::future<Item> get_item() {
std::packaged_task<Item()> task([]{
return Item(get_component().get());
});
auto future = task.get_future();
std::thread(std::move(task)).detach();
return future;
};
In general I recommend forgetting about promises and considering packaged_tasks first. A packaged_task
takes care of maintaining a (function, promise, future) triple for you. It lets you write the function in a natural way (i.e. with returns and throws and so on), and propagates the exceptions correctly into the future, which your example neglected (unhandled exceptions in any thread std::terminate
your program!).
It occurred to me that I can use std::async
with the deferred launch policy to compose the final object:
std::future<Item> get_item()
{
// start async creation of component
// (using shared_future to make it copyable)
std::shared_future<Component> component = get_component();
// deferred launch policy can be used for construction of the final object
return std::async(std::launch::deferred, [=]() {
return Item(component.get());
});
}
You can also use the then
function proposed by Herb Sutter. Here is a slightly modified version of the function. More information about how it was modified and a link to the original talk can be found in this SO question. Your code would boil down to:
return then(std::move(component), [](Component c) { return Item(c); });
The original idea is to have the function then
as a member function of std::future<T>
and there is some ongoing work of putting it into the standard. The second version of the function is for void
futures (essentially just chaining functions asynchronously). As Herb pointed out, you might pay for using this approach by potentially needing an extra thread.
Your code would look like this:
#include <future>
#include <thread>
#include <iostream>
template <typename T, typename Work>
auto then(std::future<T> f, Work w) -> std::future<decltype(w(f.get()))>
{
return std::async([](std::future<T> f, Work w)
{ return w(f.get()); }, std::move(f), std::move(w));
}
template <typename Work>
auto then(std::future<void> f, Work w) -> std::future<decltype(w())>
{
return std::async([](std::future<void> f, Work w) -> decltype(w())
{ f.wait(); return w(); }, std::move(f), std::move(w));
}
struct Component { };
struct Item {
Item(Component component) : comp(component) {}
Component comp;
};
struct Factory {
static std::future<Item> get_item() {
std::future<Component> component = get_component();
return then(std::move(component), [](Component c) { return Item(c); });
}
static std::future<Component> get_component()
{
return std::async([](){ return Component(); });
}
};
int main(int argc, char** argv)
{
auto f = Factory::get_item();
return 0;
}
The above code compiles fine with clang and libc++ (tested on Mac OS X 10.8).
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