Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a future from intermediate futures?

Tags:

c++

c++11

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.

like image 916
StackedCrooked Avatar asked Feb 14 '13 14:02

StackedCrooked


3 Answers

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!).

like image 161
R. Martinho Fernandes Avatar answered Oct 31 '22 00:10

R. Martinho Fernandes


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());
    });
}
like image 42
StackedCrooked Avatar answered Oct 31 '22 01:10

StackedCrooked


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).

like image 24
Roman Kutlak Avatar answered Oct 31 '22 02:10

Roman Kutlak