Skip to content

Commit

Permalink
Add a section on coroutines.
Browse files Browse the repository at this point in the history
  • Loading branch information
AnthonyCalandra committed Feb 15, 2021
1 parent 2be1f10 commit ee2b577
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 0 deletions.
49 changes: 49 additions & 0 deletions CPP20.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Many of these descriptions and examples come from various resources (see [Acknowledgements](#acknowledgements) section), summarized in my own words.

C++20 includes the following new language features:
- [coroutines](#coroutines)
- [concepts](#concepts)
- [designated initializers](#designated-initializers)
- [template syntax for lambdas](#template-syntax-for-lambdas)
Expand Down Expand Up @@ -34,6 +35,54 @@ C++20 includes the following new library features:

## C++20 Language Features

### Coroutines
_Coroutines_ are special functions that can have their execution suspended and resumed. To define a coroutine, the `co_return`, `co_await`, or `co_yield` keywords must be present in the function's body. C++20's coroutines are stackless; unless optimized out by the compiler, their state is allocated on the heap.

An example of a coroutine is a _generator_ function, which yields (i.e. generates) a value at each invocation:
```c++
generator<int> range(int start, int end) {
while (start < end) {
co_yield start;
start++;
}

// Implicit co_return at the end of this function:
// co_return;
}

for (int n : range(0, 10)) {
std::cout << n << std::endl;
}
```
The above `range` generator function generates values starting at `start` until `end` (exclusive), with each iteration step yielding the current value stored in `start`. The generator maintains its state across each invocation of `range` (in this case, the invocation is for each iteration in the for loop). `co_yield` takes the given expression, yields (i.e. returns) its value, and suspends the coroutine at that point. Upon resuming, execution continues after the `co_yield`.
Another example of a coroutine is a _task_, which is an asynchronous computation that is executed when the task is awaited:
```c++
task<void> echo(socket s) {
for (;;) {
auto data = co_await s.async_read();
co_await async_write(s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
```
In this example, the `co_await` keyword is introduced. This keyword takes an expression and suspends execution if the thing you're awaiting on (in this case, the read or write) is not ready, otherwise you continue execution. (Note that under the hood, `co_yield` uses `co_await`.)

Using a task to lazily evaluate a value:
```c++
task<int> calculate_meaning_of_life() {
co_return 42;
}

auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
```

**Note:** While these examples illustrate how to use coroutines at a basic level, there is lots more going on when the code is compiled. These examples are not meant to be complete coverage of C++20's coroutines. Since the `generator` and `task` classes are not provided by the standard library yet, I used the cppcoro library to compile these examples.

### Concepts
_Concepts_ are named compile-time predicates which constrain types. They take the following form:
```
Expand Down
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Many of these descriptions and examples come from various resources (see [Acknowledgements](#acknowledgements) section), summarized in my own words.

C++20 includes the following new language features:
- [coroutines](#coroutines)
- [concepts](#concepts)
- [designated initializers](#designated-initializers)
- [template syntax for lambdas](#template-syntax-for-lambdas)
Expand Down Expand Up @@ -127,6 +128,54 @@ C++11 includes the following new library features:

## C++20 Language Features

### Coroutines
_Coroutines_ are special functions that can have their execution suspended and resumed. To define a coroutine, the `co_return`, `co_await`, or `co_yield` keywords must be present in the function's body. C++20's coroutines are stackless; unless optimized out by the compiler, their state is allocated on the heap.

An example of a coroutine is a _generator_ function, which yields (i.e. generates) a value at each invocation:
```c++
generator<int> range(int start, int end) {
while (start < end) {
co_yield start;
start++;
}

// Implicit co_return at the end of this function:
// co_return;
}

for (int n : range(0, 10)) {
std::cout << n << std::endl;
}
```
The above `range` generator function generates values starting at `start` until `end` (exclusive), with each iteration step yielding the current value stored in `start`. The generator maintains its state across each invocation of `range` (in this case, the invocation is for each iteration in the for loop). `co_yield` takes the given expression, yields (i.e. returns) its value, and suspends the coroutine at that point. Upon resuming, execution continues after the `co_yield`.
Another example of a coroutine is a _task_, which is an asynchronous computation that is executed when the task is awaited:
```c++
task<void> echo(socket s) {
for (;;) {
auto data = co_await s.async_read();
co_await async_write(s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
```
In this example, the `co_await` keyword is introduced. This keyword takes an expression and suspends execution if the thing you're awaiting on (in this case, the read or write) is not ready, otherwise you continue execution. (Note that under the hood, `co_yield` uses `co_await`.)

Using a task to lazily evaluate a value:
```c++
task<int> calculate_meaning_of_life() {
co_return 42;
}

auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
```

**Note:** While these examples illustrate how to use coroutines at a basic level, there is lots more going on when the code is compiled. These examples are not meant to be complete coverage of C++20's coroutines. Since the `generator` and `task` classes are not provided by the standard library yet, I used the cppcoro library to compile these examples.

### Concepts
_Concepts_ are named compile-time predicates which constrain types. They take the following form:
```
Expand Down

0 comments on commit ee2b577

Please sign in to comment.