Best way to create a std::future<T> from a T
Asked Answered
H

3

6

I am using a std::future<T> to store the result of an optionally asynchronous operation. Depending on arguments to a function the operation is either asynchronous or synchronous. In the synchronous case I have a value that I want to store in a future. How do I best do this?

Examples given in https://en.cppreference.com/w/cpp/thread/future is:

  1. future from a packaged_task
  2. future from an async()
  3. future from a promise

But there isn't a make_future, nor does the future constructor allow creating a fulfilled future from a value. So I created a helper function for doing just that, by going through a promise like this:

template <typename T>
std::future<T> make_future(T&& t)
{
   std::promise<T> p;
   p.set_value(std::forward<T>(t));
   return p.get_future();
}

Is this a valid way to create a std::future<T> from a T?

Is there a better way to create a std::future<T> from a T?

EDIT: Example, cache:

Foo readAndCacheFoo(int id);

std::future<Foo> readFooAsync(int id)
{
   {
      const lock_guard lock{cacheMutex};
      if (id == cachedId)
      {
         return make_future(cachedFoo);
      }
   }

   return std::async(readAndCacheFoo, id);
}
Hagood answered 13/1, 2020 at 7:58 Comment(8)
A future whose promise was not fullfilled and was destroyed (and in your case it IS destroyed once the function is finished) is a broken promise so I believe this is not the way you should go for.Parkland
Furthermore, why do you even need std::future which is not associated to the std::promise?Parkland
@Parkland well, you may want to abstract out if the task is async or sync, the future makes perfect sense in that situationOverprize
Perhaps use a std::packaged_task, get the future from it, execute the task and return the future?Silkweed
Honestly, I don't find std::future useful in general. Has too little functionality and slow. There are many ways you can store result of an ansynchronous/synchronous process without std::future.Foliole
Thanks for the example. Note that this approach will likely be very inefficient, since once the calculated value is cached, you will return it via future made by a promise, which creates a shared state (and, therefore, involves dynamic memory allocation).Planimetry
@DanielLangr Yes, it is probably very inefficient, but it would be a lot faster than retrieving Foo over a network with high latency.Hagood
@Hagood Understand. Just it might be faster, for example, to return std::variant<std::reference_wrapper<Foo>,std::future<Foo>> and return a future only when it is not cached. Othewise, return a reference to the cached object. Need to admit that I don't know whether there isn't some standard/pattern solution of this problem.Planimetry
O
3

This can be done pretty easily but it is a bit convoluted:

template <typename T>
std::future<T> make_future(T&& t)
{
    auto fun = [val=std::forward<T>(t)]() { return val; };
    std::packaged_task<T()> task(std::move(fun));
    auto future = task.get_future();
    task();
    return future;
}

Note that std::future and packaged_task are supposed to share internal state so this should be safe

EDIT: actually it seems your code is valid as well and even simpler

Overprize answered 13/1, 2020 at 8:4 Comment(1)
Which constructor of std::future is that supposed to call?Goral
F
3

Is this a valid way to create a std::future from a T?

Yes, it is.

However, I would say that 'make_future' might not be the most appropriate name. The whole idea of the 'future' class is that it's value might be ready in the future while your function always returns an already fulfilled future.

Well, it's just a name... Afaik, your logic is perfectly fine.

Edit: There is a C++ proposal for make_ready_future and make_exceptional_future functions. P0159R0

Fennelflower answered 13/1, 2020 at 9:42 Comment(1)
S
2

While answer of @bartop with packaged_task is absolute correct, let me pay your attention to the existing std function:

auto my_shine_future = std::async(std::launch::deferred, [...](args..., ){...} ...);

std::launch::deferred - creates lazy evaluation without additional thread.

Suttle answered 25/11, 2023 at 8:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.