Создание будущего из промежуточных фьючерсов?
В следующем примере кода я хочу создать объект Item
из 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();
};
Как перейти от std::future<Component>
в std::future<Item>
?
Обновление: удалила мою первую идею (которая была основана на потоках) из вопроса и отправила ответ.
Ответы
Ответ 1
Мне пришло в голову, что я могу использовать std::async
с отложенной политикой запуска для создания конечного объекта:
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());
});
}
Ответ 2
Требуется 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;
};
В общем, я рекомендую забыть о promises и сначала рассмотреть packaged_tasks. A packaged_task
заботится о том, чтобы поддерживать (функцию, обещание, будущее) тройку для вас. Это позволяет вам писать функцию естественным образом (т.е. С помощью возвратов и бросков и т.д.) И правильно распространять исключения в будущем, которые ваш пример игнорировал (необработанные исключения в любом потоке std::terminate
вашей программы!).
Ответ 3
Вы также можете использовать функцию then
, предложенную Herb Sutter. Вот немного измененная версия функции. Более подробную информацию о том, как он был изменен, и ссылку на исходный разговор, можно найти в этом вопросе SO. Ваш код будет сжиматься до:
return then(std::move(component), [](Component c) { return Item(c); });
Первоначальная идея состоит в том, чтобы функция then
была как функция-член от std::future<T>
, и есть некоторая работа по ее включению в стандарт. Вторая версия функции предназначена для фьючерсов void
(по сути, просто асинхронно цепочки). Как отметил Херб, вы можете заплатить за использование этого подхода, потенциально нуждаясь в дополнительном потоке.
Ваш код будет выглядеть так:
#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;
}
Вышеупомянутый код отлично компилируется с помощью clang и libС++ (протестирован в Mac OS X 10.8).