Let's assume I have a coroutine function with a single co_await
operator in the body waiting on an event, like this:
auto coro = [&]()->coroutine {
co_await evt;
std::cerr << "event was triggered\n";
};
I need to write a function void forever(auto&&)
which takes a coroutine (forever(coro);
) and executes it in an infinite loop, having the same effect as a coroutine with an infinite loop inside the body, i.e.
auto forever_coro = [&]->coroutine {
for(auto counter = 1;;++counter) {
co_await evt;
std::cerr << "event was triggered " << counter << " time(s)\n";
}
};
Update: forever()
obviously has to be a coroutine itself, which must somehow be resumed when the callee is done. So the awaiter for that should probably be resumed when callee.done()
returns true
, but how that would trigger the resumption? The forever()
function would look something like this:
auto forever(auto&& coro)->coroutine {
for(;;) co_await callee_awaiter(coro);
}
So basically the question becomes, how that callee_awaiter
can be implemented?
I've adapted your sample into an incomplete but working implementation of awaitable coroutine
:
struct coroutine;
struct promise
{
struct {
bool done{false};
std::coroutine_handle<void> suspended_awaiter{};
bool await_ready() noexcept {
// We always suspend in the final awaiter to avoid the promise being destructed
return false;
}
std::coroutine_handle<void> await_suspend(std::coroutine_handle<void>) noexcept {
return suspended_awaiter ? std::move(suspended_awaiter) : std::noop_coroutine();
}
void await_resume() noexcept {}
} final_awaiter;
coroutine get_return_object();
std::suspend_never initial_suspend() noexcept { return {}; }
// return a final awaiter that's responsible for invoking continuation coroutines
auto& final_suspend() noexcept { return final_awaiter; }
void return_void() {}
void unhandled_exception() {}
};
struct coroutine
{
using promise_type = ::promise;
promise_type& promise;
bool await_ready() noexcept {
return promise.final_awaiter.done;
}
void await_suspend(std::coroutine_handle<promise_type> suspended) {
promise.final_awaiter.suspended_awaiter = std::move(suspended);
}
void await_resume() {}
};
coroutine promise::get_return_object() { return {*this}; }
coroutine forever(auto&& crtn)
{
while (true) {
co_await crtn();
}
}
The incomplete part is that I've taken a shortcut to manage the promise lifetime. Namely: the coroutine relies on it's promise reference to stay valid even after completion. The way I guarantee that is by always suspending in final_suspend
. This means that the coroutine_handle::destroy()
must now be explicitly called to free the coroutine state. I have left that out of the code. If coroutine
is neither movable nor copyable, you can just put the destroy call into ~coroutine
. Otherwise you'll have to work out the lifetime you want yourself. If you end up needing help with that, then IMO that's a separate question.
Also, I've completely ignored thread safety for now, because you didn't mention anything about it.