Question

I have a helper function which I use to "flatten" nested futures into a single future:

EDIT: Renamed "fold" to "flatten" as suggested.

I'm using the futures from the boost library:

template<typename T>
auto flatten(boost::unique_future<T>&& f) -> boost::unique_future<decltype(f.get().get())>
{
    auto shared_f = boost::shared_future<T>(std::move(f));
    return async(launch_policy::deferred, [=]() mutable
    {
        return shared_f.get().get();
    });
}

It is used like this:

auto nested_future_result = async([]() -> boost::shared_future<int>
{
    auto tmp = async([]
    {
        return 1;
    });
    return boost::shared_future<int>(std::move(tmp));    
});

boost::unique_future<int> future_result = flatten(nested_future_result);
int result = future_result.get();

The problem is that this only works if I convert the "nested" future into a shared_future. Is there any nice way to get around it? What I would like is something like:

auto future_result = flatten(async([]
{
    return async([]
    {
        return 1;
    }); 
}));

int result = future_result.get();

Secondly, I am a bit unsure of the name for the method. Any opinions?

Was it helpful?

Solution

(Note: I don't understand how you got boost::unique_future to cooperate with std::async so I replaced all instances of boost::unique_future with std::future. The code has been tested and works on my end.)

The issue is that lambda expressions either capture by value (which really means capture by copy) or by reference (not applicable here as we want to tie the lifetime of the future to our closure), whereas std::future is move-only. The answer to that is usually std::bind, although in this case std::async has a built-in bind-like functionality:

template<typename T>
auto fold(std::future<T>&& f)
-> std::future<decltype(f.get().get())>
{
    return std::async(std::launch::deferred, [](std::future<T>&& f)
    {
        return f.get().get();
    }, std::move(f));
}

I don't have a good name to recommend I'm afraid. If the template perhaps worked recursively to transform any std::future<std::future<std::future<...std::future<T>...>>> to std::future<T> then perhaps I'd call if flatten_future. Or perhaps simply flatten, since after all it only accepts an std::future in the first place.


Assuming we already have a unary async:

template<typename Functor, typename Arg, typename... Args>
auto async(Functor&& functor, Arg&& arg, Args&&.... args)
-> decltype( async(std::bind(std::forward<Functor>(functor)
    , std::forward<Arg>(arg), std::forward<Args>(args)...)) )
{
    return async(std::bind(std::forward<Functor>(functor)
        , std::forward<Arg>(arg), std::forward<Args>(args)...));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top