An Introduction to C++ Coroutines
Learn about C++ coroutines, including coroutine abstraction, and learn to differentiate them from subroutines through examples.
We'll cover the following...
A few motivating examples
Coroutines are one of those features, similar to lambda expressions, that offer a way to completely change the way we write and think about C++ code. The concept is very general and can be applied in many different ways. To give you a taste of how C++ can look when using coroutines, we will here look briefly at two examples.
Yield-expressions can be used for implementing generators—objects that produce sequences of values lazily. In this example, we will use the keywords co_yield
and co_return
to control the flow:
auto iota(int start) -> Generator<int> {for (int i = start; i < std::numeric_limits<int>::max(); ++i) {co_yield i;}}auto take_until(Generator<int>& gen, int value) -> Generator<int> {for (auto v : gen) {if (v == value) {co_return;}co_yield v;}}int main() {auto i = iota(2);auto t = take_until(i, 5);for (auto v : t) {std::cout << v << ", ";}return 0;}
In the preceding example, iota()
and take_until()
are coroutines. iota()
generates a sequence of integers and take_until()
yields values until it finds the specified value. The Generator
template is a custom type that we will show you how to design and implement later on in this chapter.
Building generators is one common use case for coroutines; another one is implementing asynchronous tasks. The next example will demonstrate how we can use the operator co_await
to ...