Reputation: 51
A typical coroutine object looks like this:
struct Coroutine
{
struct promise_type
{
int value;
Coroutine get_return_object() {return this; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_value(int v) {
value = v;
}
void unhandled_exception() {};
};
Coroutine(promise_type *p) : handle(std::coroutine_handle<promise_type>::from_promise(*p)) {
};
~Coroutine() {
handle.destroy();
}
std::coroutine_handle<promise_type> handle;
};
Why is the promise_type
separated from the coroutine. Does it make sense to have multiple coroutines for a single promise, or multiple promises for a single coroutine? Is there a use case for that?
Because otherwise, if only one promise can exist per coroutine, wouldn't it have been enough to embed the return value into the Coroutine
structure, e. g.:
struct Coroutine
{
int value;
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_value(int v) {
value = v;
}
void unhandled_exception() {};
};
Upvotes: 1
Views: 1465
Reputation: 473946
The return value of a coroutine is distinct from the promise type for several reasons.
That a function is a coroutine is intended to be an implementation detail of the function. Nothing in a function declaration requires it to be a coroutine. Including the return type. This means that a function that returns a coroutine future type may or may not be a coroutine. This allows for things like passing a coroutine through you; you call some function that itself schedules its resumption, and you just return its return value as is.
Separating the two is intended to allow existing types that support continuations to be re-implemented internally as coroutines. Or more generally, to allow a type to represent any kind of asynchronous process without caring about whether its a coroutine or not. For example, if std::future<T>
permitted .then
continuations, one could easily allow you to co_await
on them by just having the co_await
machinery use .then
to schedule the coroutine's resumption. But you could also just call .then
directly with a regular function.
If the return value object was the coroutine, it would be impossible (without dynamic polymorphism of some form) to make such a change internally without an external change to the interface.
Also, you seem to be mistaking the return value of a function that happens to be a coroutine with the coroutine itself. This is not the case. The coroutine is the actual call stack allocated by the coroutine machinery, which includes the promise object. The return value is linked to the coroutine, but it isn't the coroutine itself. The coroutine and its promise object are (usually) heap-allocated and stored elsewhere, with the return value merely referencing it.
Upvotes: 4