# Coroutines TS Customization Points
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](https://lewissbaker.github.io/2017/09/25/coroutine-theory) of [blog](https://lewissbaker.github.io/2017/11/17/understanding-operator-co-await) [posts](https://lewissbaker.github.io/2018/09/05/understanding-the-promise-type). Or if you are feeling brave, just go read the current [working draft](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4775.pdf) of the TS.
This is also intended for people who need to know the inner workings of the TS, such as people *writing* [libraries](https://github.com/lewissbaker/cppcoro). This is not for people who just want to *use* coroutines with one of these libraries, since that is *much* simpler.
## Table of customization points
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](#Coroutine-function-transformation). These are split into sections based on what the customization point is attached to.
| Name | Required | Attached To | Notes |
| - | - | - | - |
| `get_return_object()` | :heavy_check_mark: | 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()` | :heavy_check_mark: | Promise | Argument is what `co_return` takes |
| `unhandled_exception()` | :heavy_check_mark: | Promise | |
| `yield_value()` | with `co_yield` | Promise | |
| `await_transform()` | | Promise | |
| `initial_suspend()` | :heavy_check_mark: | Promise | |
| `final_suspend()` | :heavy_check_mark: | 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 |
| <hr> | <hr> | <hr> | <hr>
| `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.|
| <hr> | <hr> | <hr> | <hr>
| `operator co_await()` | | ProtoAwaitable or ADL free-function | Converts a ProtoAwaitable to an Awaitable |
| <hr> | <hr> | <hr> | <hr>
| `await_ready()` | :heavy_check_mark: | Awaitable | |
| `await_suspend()` | :heavy_check_mark: | Awaitable | Only called if `await_ready()` returns `false`. Bool return has **opposite** meaning as `await_ready()` (ie. `true` means suspend)|
| `await_resume()` | :heavy_check_mark: | Awaitable | |
## Table of `std::` things
| Name | Meaning |
| - | - |
| `coroutine_traits<T>::promise_type` | `T::promise_type` unless specialized |
| [`coroutine_handle<PromiseType>`](#coroutine_handleltPromiseTypegt) | 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>`
```cpp
template <class Promise = void>
struct coroutine_handle;
/**
* The type-erased base type for all coroutine_handles.
* Allows libraries that don't care about the Promise type to deal
* with a single concrete type which represents all coroutines.
*/
template <>
struct coroutine_handle<void> // Usually written as coroutine_handle<>
{
/// Make a null coroutine_handle.
constexpr coroutine_handle() noexcept;
constexpr coroutine_handle(nullptr_t) noexcept;
coroutine_handle& operator=(nullptr_t) noexcept;
/// Is this a null coroutine_handle?
constexpr explicit operator bool() const noexcept;
/// Convert to/from an opaque address. Useful with C APIs.
constexpr void* address() const noexcept;
constexpr static coroutine_handle from_address(void* addr);
//
// Remaining methods can only be called while the coro is suspended!
//
/// Is the coro suspended at the `final_suspend()` point?
/// WARNING: this should never be used for coroutines that may return
/// `suspend_never` from `final_suspend()`, since it will either return
/// true or dereference a pointer to deleted memory!
bool done() const;
/// These both resume the coroutine where is last suspended.
/// coroutine_handle objects can be treated as a `void()` function
/// object, which simplfies integration into existing libraries
/// which take the equivalent of a `std::function<void()>`.
void resume() const;
void operator()() const { resume(); }
/// Destroy any local variables currently in scope inside the coroutine
/// and delete the allocated frame buffer. This will automatically be
/// called if resumed following the call to `final_suspend()`, or if
/// that doesn't actually suspend. May be called any time the coroutine
/// is supsended, not just at `final_suspend()`, however not all coroutines
/// will be prepared to not wake up after suspending part way through, so
/// This should only be done in cases where it is known to be safe.
void destroy() const;
};
/**
* The specific type used by the coroutine machinery.
*/
template <class Promise>
struct coroutine_handle
{
/// Can be constructed/retrieved the same as above.
constexpr coroutine_handle() noexcept;
constexpr coroutine_handle(nullptr_t) noexcept;
coroutine_handle& operator=(nullptr_t) noexcept;
constexpr void* address() const noexcept;
constexpr static coroutine_handle from_address(void* addr);
/// Also supports conversion to/from a reference to the Promise.
/// This is the only API gained by not type erasing.
Promise& promise() const;
static coroutine_handle from_promise(Promise&);
/// Implicit conversion to erase Promise type
constexpr operator coroutine_handle<>() const noexcept;
/// Also provides the API from coroutine_handle<void>
constexpr explicit operator bool() const noexcept;
bool done() const;
void operator()() const;
void resume() const;
void destroy() const;
};
/// coroutine_handles are ordered by `addres()`. Maybe we'll get a <=> as well?
constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept;
constexpr bool operator!=(coroutine_handle<> x, coroutine_handle<> y) noexcept;
constexpr bool operator<(coroutine_handle<> x, coroutine_handle<> y) noexcept;
constexpr bool operator>(coroutine_handle<> x, coroutine_handle<> y) noexcept;
constexpr bool operator<=(coroutine_handle<> x, coroutine_handle<> y) noexcept;
constexpr bool operator>=(coroutine_handle<> x, coroutine_handle<> y) noexcept;
template <class Promise> struct hash<experimental::coroutine_handle<Promise>>;
```
## Coroutine function transformation
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_return), [`co_await`](#co_await), or [`co_yield`](#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.
```cpp
// If `func` is a method on some Type, pretend it is just a free funtion
// with a `Type&` argument before the declared arguments.
MyCoroType func(Args... args) {
using PromiseType = std::coroutine_traits<MyCoroType, Args...>::promise_type;
// All local variables are actually constructed inside this buffer.
// The arguments to `func()` will also be copied into this buffer.
// The allocation may be elided if the compiler can prove it safe.
// `operator new` will first be looked up in PromiseType, then globally.
// If the found name is not callable with `args...`, it will try again
// without passing them. This is known as "parameter preview" and allows
// things like passing the allocator that should be used.
void* coro_frame;
size_t coro_frame_size = /*...*/;
if constexpr (PromiseType::get_return_object_on_allocation_failure exists) {
coro_frame = operator new(coro_frame_size,
std::nothrow,
args...);
if (!coro_frame) {
return PromiseType::get_return_object_on_allocation_failure();
}
} else {
coro_frame = operator new(coro_frame_size,
args...);
}
// Will fall back to `PromiseType()` if it can't
// be constructed like this. Note that the promise object
// will be allocated in the coro_frame buffer and will live
// as long as the coroutine. This makes a handy place to
// stash coordination state that would normally go in
// some refcounted SharedState object referenced from both
// the promise and MyCoroType objects. If you do this,
// make sure that `final_suspend()` actually suspends!
auto promise = PromiseType(args...);
auto my_handle = std::coroutine_handle<PromiseType>::from_promise(promise);
auto protoReturnValue = promise.get_return_object();
// This is the value that is actually returned to the caller
// of `func()`. It is unspecified whether this conversion
// happens before calling `initial_suspend()` or the first
// time the coroutine actually suspends or returns, which
// is when `func()` returns to its caller. If it is called
// late, you can use that to optimize for cases where the
// coro completes without ever suspending.
MyCoroType actualReturnValue = std::move(protoReturnValue);
// This may move inside the try block in a future version of the TS.
co_await promise.initial_suspend();
try {
// This will contain usually a call to `promise.return_void()`
// or `promise.return_value()`. See co_return below.
BODY;
if constexpr (promise has return_void()) {
// Make rolling off the end of BODY well defined for
// coros that (logically) return `void`. For other
// kinds ofcoros this is UB that your compiler should
// warn about, just like in normal functions.
co_return;
}
} catch (...) {
// This can get the exception using either `throw;`
// or `std::current_exception()`. If this throws,
// the coroutine is considered suspended at its final
// suspend point, without calling final_suspend().
promise.unhandled_exception();
}
co_await promise.final_suspend();
// It is common for coroutines to never be resumed from
// this call if they suspend here. In that case, the
// following line will never execute, and something
// else must free the storage by calling `destory()`
// on the coroutine handle.
// Will fallback to single-argument form.
operator delete(coro_frame, coro_frame_size);
}
```
### `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.
```cpp
[&]() -> decltype(auto) { // Immediately invoked below
// Note: The await_trasnform operation is only done on
// users' co_awaits, not the implicit co_awaits on the
// returns from yield_value, initial_suspend, and
// final_suspend, because the promise is already involved
// in those.
auto&& Awaitable awaitable =
/*operator co_await*/ (
/*promise.await_transform*/ (
makeProtoAwaitable()
)
);
if (!awaitable.await_ready()) {
// If await_ready() returns false, the coroutine enters a "suspended" state.
// This means that it is legal to do things like call destroy() or
// resume() on the handle passed to await_suspend(). However, rather than
// calling resume(), it is better to return the handle so that a tail-call
// is made rather than recursively growing the stack.
using SuspendRet = decltype(awaitable.await_suspend(my_handle));
if constexpr (is_void<SuspendRet>()) {
awaitable.await_suspend(my_handle)
SUSPEND_HERE();
} else if constexpr (is_bool<SuspendRet>()) {
if (awaitable.await_suspend(my_handle)) {
SUSPEND_HERE();
}
} else {
auto dest = awaitable.await_suspend(my_handle);
if (dest is a noop_coroutine_handle) {
SUSPEND_HERE();
} else {
// This is effectively a no-op if dest == my_handle
SUSPEND_HERE_AND_TAIL_JUMP_TO(dest);
}
}
// Once we get here, either by being resumed, or never reaching a SUSPEND,
// the coroutine leaves the "suspended" state, and it is no longer legal
// to call resume() or destroy() on the handle.
}
return awaitable.await_resume(); // May return void
}();
```
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.
## Templates
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.
### Promise
```cpp
template <typename T>
struct promise_type {
// If you care about parameter preview
template <typename... Args>
promise_type(Args&&...);
auto initial_suspend() { return std::experimental::suspend_always(); }
auto final_suspend() { return std::experimental::suspend_always(); }
auto get_return_object() { return MyCoroType(this); }
void return_value(T val) {}
void unhandled_exception() {}
// If you need to see ProtoAwaitables before `operator co_await`
// is called on them.
template <typename T>
auto await_transform(T&& awaitable);
// If you want to control allocation
template <typename... Args>
void* operator new(size_t size, Args...);
void operator delete(void* p, size_t size);
// Only if you care about recovering from allocation failure
static MyCoroType get_return_object_on_allocation_failure();
};
struct promise_type /*void*/ {
// If you care about parameter preview
template <typename... Args>
promise_type(Args&&...);
auto initial_suspend() { return std::experimental::suspend_always(); }
auto final_suspend() { return std::experimental::suspend_always(); }
auto get_return_object() { return MyCoroType(this); }
void return_void() {}
void unhandled_exception() {}
// If you want to support co_yield.
// You rarely have this in the same type as return_value.
void yield_value(T) {}
// If you need to see ProtoAwaitables before `operator co_await`
// is called on them.
template <typename T>
auto await_transform(T&& awaitable);
// If you want to control allocation
template <typename... Args>
void* operator new(size_t size, Args...);
void operator delete(void* p, size_t size);
// Only if you care about recovering from allocation failure
static MyCoroType get_return_object_on_allocation_failure();
};
```
### Awaitable
```cpp
template <typename T>
struct MyAwaitable {
bool await_ready() { return false; }
void await_suspend(coroutine_handle<> h) {}
T await_resume() {}
};
```
### Future-like type
```cpp
template <typename T>
struct MyFuture {
struct promise_type; // see above
// Can also just *be* an Awaitable with no `operator co_await`
MyAwaitable<T> operator co_await(); // see above
};
```
### Generator (Range) type
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](https://wg21.link/p2529r0) by the same author as this page for more info.
```cpp
template <typename T>
struct generator {
struct promise_type {
suspend_never initial_suspend() { return {}; }
suspend_always final_suspend() { return {}; }
auto yield_value(T v) {
val.emplace(std::move(v));
return suspend_always();
}
auto get_return_object() { return generator(this); }
void return_void() {}
void unhandled_exception() { throw; }
auto coro() { return coroutine_handle<promise_type>::from_promise(*this); }
std::optional<T> val;
};
generator(generator&& source) : p(std::exchange(source.p, nullptr)) {}
explicit generator(promise_type* p) :p(p) {}
~generator() {
if (p) p->coro().destroy();
}
// generator<T> is a Range. You can use it in a range-for loop.
struct EndSentinel{};
auto end() { return EndSentinel(); }
auto begin() {
struct Iter {
// You probably want to add iterator_tag and friends.
// That isn't needed if you only want to support range-for.
bool operator!=(EndSentinel) const { return !p->coro().done(); }
void operator++() { p->coro().resume(); }
T& operator*() const { return *p->val; }
promise_type* p;
};
return Iter{p};
}
promise_type* p;
};
```