This is intended to be a reference/cheatsheet/decoder ring, not an introduction. If you don't know anything about the Coroutine TS, I suggest reading Lewis Baker's excellent series of blog posts. Or if you are feeling brave, just go read the current working draft of the TS.
This is also intended for people who need to know the inner workings of the TS, such as people writing libraries. This is not for people who just want to use coroutines with one of these libraries, since that is much simpler.
A few long names are split up so that the table formats better. All names should be considered a single possibly-qualified identifier. Everything is explained in more detail below. These are split into sections based on what the customization point is attached to.
Name | Required | Attached To | Notes |
---|---|---|---|
get_return_object() |
Image Not Showing
Possible Reasons
|
Promise | What gets returned from the coroutine (eg lazy<T> or generator<T> ) |
get_return_object_on _allocation_failure() |
Promise (static) | If present, std::nothrow will be passed to operator new |
|
return_value() or return_void() |
Image Not Showing
Possible Reasons
|
Promise | Argument is what co_return takes |
unhandled_exception() |
Image Not Showing
Possible Reasons
|
Promise | |
yield_value() |
with co_yield |
Promise | |
await_transform() |
Promise | ||
initial_suspend() |
Image Not Showing
Possible Reasons
|
Promise | |
final_suspend() |
Image Not Showing
Possible Reasons
|
Promise | |
Constructor() |
can be defaulted | Promise | Either nullary (default ctor) or gets to "preview" coro parameters |
operator new() |
Promise or global | Also gets "parameter preview" | |
operator delete() |
Promise or global | No "parameter preview", but supports sized-deallocation | |
std:: coroutine_traits<T, Args...>::promise_type |
Only provide this if you can't add a nested promise_type on your coroutine type. Also gets "parameter preview" for types. |
||
operator co_await() |
ProtoAwaitable or ADL free-function | Converts a ProtoAwaitable to an Awaitable | |
await_ready() |
Image Not Showing
Possible Reasons
|
Awaitable | |
await_suspend() |
Image Not Showing
Possible Reasons
|
Awaitable | Only called if await_ready() returns false . Bool return has opposite meaning as await_ready() (ie. true means suspend) |
await_resume() |
Image Not Showing
Possible Reasons
|
Awaitable |
std::
thingsName | Meaning |
---|---|
coroutine_traits<T>::promise_type |
T::promise_type unless specialized |
coroutine_handle<PromiseType> |
A magical type that lets you resume or destroy a suspended coroutine. Can also get to or from a PromiseType& |
coroutine_handle<> |
Type-erased version of above. The unerased type implicitly converts to the erased type. This conversion is harmless and encouraged when you don't need access to a Promise |
noop_coroutine_handle |
A coroutine_handle that when returned from await_suspend() behaves as-if void or true were returned |
noop_coroutine() |
Returns a noop_coroutine_handle |
suspend_always |
A trivial Awaitable that always suspends and yields void . Useful for returning from initial_suspend() , final_suspend() , and yield_value() |
suspend_never |
A trivial Awaitable that never suspends and yields void . Useful for returning from initial_suspend() and final_suspend() |
coroutine_handle<PromiseType>
A coroutine function like MyCoroType func(Args... args) { BODY; }
is transformed to something roughly like the following. This is done for any function where BODY
contains any of co_return
, co_await
, or co_yield
(which is really also just a co_await
).
The detailed expansions below may refer back to local variables declared here. The names are not actually visible to the user-provided BODY
statements.
co_await
The expression co_await makeProtoAwaitable()
expands to roughly the following. Commented out portions are optional and will be used if present. SUSPEND_HERE()
is shorthand for returning from the call to the function that creates the coroutine object, or from the call to resume()
on the handle. SUSPEND_HERE_AND_TAIL_JUMP_TO(dest)
is shorthand for doing SUSPEND_HERE()
and then after poping the stack frame calling dest.resume()
without actually returning control to the caller/resumer.
Yes operator co_await()
is optional, and yes that does mean that often co_await
expressions will not actually call any operator co_await
.
co_yield
The expression co_yield value
expands to co_await promise.yield_value(value)
(see above for how to expand the co_await
). Promises will usually just return suspend_always()
here, however that isn't required. If it returns a non-void ProtoAwaitable, you have a two-way generator that supports things like auto input = co_yield output;
While similar to co_await
, there is one notable grammatical difference due to their different intended usages. co_await
binds tightly to its argument like any other unary operator, such as *expr
. co_yield
binds loosely like return expr
. So co_await a + b
is parsed as (co_await a) + b
, while co_yield a + b
is parsed as co_yield (a + b)
. In most cases you don't need to worry about this since they behave how you would expect, but be careful with complex expressions if you are using either in a "weird" way.
co_return
The statement co_return;
is rewritten to promise.return_void();
and co_return expr;
is rewritten to promise.return_value(expr);
. In both cases, exection will then leave the BODY;
of the transformed coroutine, and go to the call to promise.final_suspend()
.
This is logically like normal return
except in a coroutine. Normal return
is banned in a coroutine, and co_return
can't be in a non-coroutine function because it will turn it into a coroutine.
These are some templates (not in the c++ sense) ready for copy-pasting! They don't need to be a templates (in the c++ sense), but often are, or are inner types of templates.
Nota Bene: These have not been tested. Treat them as slideware rather than as production code.
These use co_yield
. While this returns T&
from the iterator's operator*()
and stores a copy of the value, a real implementation should strongly consider returning T&&
and storing a pointer, as is done by std::generator
. See P2529 by the same author as this page for more info.