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.


Example
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 promise_type = Promise
using coro_handle = std::coroutine_handle<promise_type>
using erased_coro_handle = std::coroutine_handle<>
using return_value = typename deduce_type<typename Promise::returns>::type

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_awaiting the corountine, it will cause undefined behaviour.