std::shared_future
is when you need to have multiple valid copies of a std::future
, and possibly multiple consumers of said std::future
.
You can move a std::future
into a std::shared_future
.
Consider std::future
:std::shared_future
to be like std::unique_ptr
:std::shared_ptr
.
std::promise
is one way to generate a std::future
.
template<class T>
std::future<std::decay_t<T>> make_ready_future( T&& t ) {
std::promise<std::decay_t<T>> pr;
auto r = pr.get_future();
pr.set_value(std::forward<T>(t));
return r;
}
this is a toy example of std::promise
, where I take a T
and I generate an already-ready std::future<T>
from it.
If you have code that could require threading off, or might already be calculated, you can return a std::future<T>
from it. If it isn't calculated, you return the result of a std::async( ... )
call. If it is already calculated, you return a make_ready_future( calculated value )
.
A slightly more complex situation:
template<class T>
struct promise_pipe {
std::future<T> out;
std::function<void(T)> in;
promise_pipe() {
auto pr = std::make_shared<std::promise<T>>();
out = pr->get_future();
in = [pr](T t)mutable{
if (pr) pr->set_value(std::forward<T>(t));
pr = {};
};
}
};
Here I have written a promise_pipe
.
A promise_pipe<int>
has two fields -- a std::future<int> out
and a std::function<void(int)> in
.
The out
future will be ready if and only if you call in
with an int
.
So you can have a function that returns a std::future<Bitmap>
. You have some network code that is expecting the bitmap to be streamed. So you create a promise_pipe<Bitmap>
, return the std::future<Bitmap>
and store the in
in a "todo" list when the bitmap arrives.
When the bitmap has fully arrived, you just do in(Bitmap)
and whomever is waiting on that future
will be notified.
promise
s are objects designed to allow programmers to generate their own future
s, not just use ones created by the std
library.
Now a packaged_task
is often easier to work with than a std::promise
, and serves much the same purpose as the promise_pipe
above. It is move-only, so you can end up having to wrap it into a std::shared_ptr
and a std::function
anyhow (or a custom move-only std::function
)