reusable_future
A function can mark itself as a coroutine by having a return type of reusable_future<T, Promise>
. A reusable future is essentially simple_future<T, Promise>
that can be co_await
’ed multiple times. The return type is always wrapped in an std::optional
since the reusable_future<T, Promise>
may have finished or hit an error. A user can use the bool complete()
to distinguish the two. A reusable_future<T, Promise>
can be viewed as a generator that may exhibit asynchronous behavior. A reusable_future<T>
will wake up the waiting coroutine and return a value to when the Promise
uses either the co_yield
or co_return
keywords. co_yield
will return a value, but allow a the reusable_future<T, Promise>
to be co_await``ed again. ``co_return
will complete the coroutine and any further co_await
attempts on the reusable_future<T, Promise>
will fail.
The difference in use cases between using a simple_future<T. Promise>
many times and having a single reusable_future<T, Promise>
is that a reusable_future<T, Promise>
can store state about its context between value returns.
The promise type for an reusable_future<T, Promise>
must satisfy the Reoccurring
concept. The default promise type is the reusable_promise.
simple_future<std::string>
attempt_foo() noexcept;
reusable_future<std::size_t>
your_class::exponential_delay(std::size_t _max) noexcept
{
std::size_t delay = 2;
while (delay < _max)
{
co_await yield(order_t(order::now() + order::seconds(delay)));
delay *= 2;
co_yield delay;
}
/* dealyed to long... */
co_return std::nullopt_t;
}
async_function<>
your_class::run() noexcept
{
while (auto delay_func = exponential_delay(256); !delay_func.complete())
{
std::optional<std::string> foo = co_await attempt_foo();
if (foo)
{
std::cout << "Got foo: " << *foo << "\n";
delay_func.force_completion();
}
else
{
std::optional<std::size_t> delay_length = co_await delay_func;
if (delay_length) { std::cout << "Delayed for: " << *delay_length << "\n"; }
else
{
std::cout << "Timed out\n";
}
}
}
}
-
template<typename T = void, details::Reoccurring Promise = reusable_promise<T>>
class zab::reusable_future Represents the future value of a reusuable promise.
- Template Parameters
T – The type of the promised value.
Public Types
-
using coro_handle = std::coroutine_handle<promise_type>
-
using erased_coro_handle = std::coroutine_handle<>
Public Functions
-
inline reusable_future(coro_handle _coroutine)
Construct with the future with a handle to its coroutine.
- Parameters
_coroutine – [in] The coroutine handle.
-
inline ~reusable_future()
Destroys the future and cleans up the coroutine handle.
We destroy the coroutine handle here as the the final_suspend in the
reusable_promise
does not resume.
-
inline reusable_future &operator=(reusable_future &&_other)
Move Assignment operator.
- Parameters
_other – The simple_future to move.
- Returns
*this.
-
reusable_future(const reusable_future &_other) = delete
Cannot be coppied.
- Parameters
_other – [in] The reusable_future to copy.
-
inline reusable_future(reusable_future &&_other)
Moving makes the moved reusable_future lose ownership of the handle.
- Parameters
_other – The reusable_future to move.
-
inline auto operator co_await() const noexcept
wait for the
reusable_promise
to be fulfilled or fail.Then co_await returns an
std::optional<T>
which represents if the reusable_promise was fulfilled.- Returns
A
co_await
’able struct.
-
inline bool complete() const noexcept
Test if the coroutine has fully completed.
- Returns
true if complete, false otherwise.
-
inline operator bool() const noexcept
Test if the coroutine has fully completed.
- Returns
true if complete, false otherwise.
-
inline void force_completion() noexcept
Force the end of the coroutine.
If this function is called while somthing is
co_await
ing the corountine, it will cause undefined behaviour.