https://github.com/sjinks/coro-cpp
Yet Another C++20 Coroutine Library
https://github.com/sjinks/coro-cpp
async-await asynchronous-programming coroutines cpp cpp20 cpp20-coroutine
Last synced: 5 months ago
JSON representation
Yet Another C++20 Coroutine Library
- Host: GitHub
- URL: https://github.com/sjinks/coro-cpp
- Owner: sjinks
- License: mit
- Created: 2025-02-03T12:12:04.000Z (12 months ago)
- Default Branch: master
- Last Pushed: 2025-06-11T18:03:00.000Z (7 months ago)
- Last Synced: 2025-06-11T19:52:32.898Z (7 months ago)
- Topics: async-await, asynchronous-programming, coroutines, cpp, cpp20, cpp20-coroutine
- Language: C++
- Homepage: https://sjinks.github.io/coro-cpp/
- Size: 144 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# WWA Coro — Yet Another C++20 Coroutine Library
[](https://github.com/sjinks/coro-cpp/actions/workflows/ci.yml)
[](https://github.com/sjinks/coro-cpp/actions/workflows/codeql.yml)
[](https://sonarcloud.io/summary/new_code?id=sjinks_coro-cpp)
This project provides a set of coroutine-based asynchronous utilities and generators for C++20. It includes synchronous and asynchronous generators, tasks, and various utilities to facilitate coroutine-based programming.
## Features
- **Synchronous Generators**: Create coroutine-based generators that produce values synchronously.
- **Asynchronous Generators**: Create coroutine-based generators that produce values asynchronously.
- **Tasks**: Manage coroutine-based tasks that produce values of a specified type.
## Getting Started
### Prerequisites
- C++20 compatible compiler
- CMake 3.23 or higher
### Installation
```sh
cmake -B build
cmake --build build
sudo cmake --install build
```
#### Configuration Options
| Option | Description | Default |
|-------------------------|---------------------------------------------------------------------------|---------|
| `BUILD_TESTS` | Build tests | `ON` |
| `BUILD_EXAMPLES` | Build examples | `ON` |
| `BUILD_DOCS` | Build documentation | `ON` |
| `BUILD_INTERNAL_DOCS` | Build internal documentation | `OFF` |
| `ENABLE_MAINTAINER_MODE`| Maintainer mode (enable more compiler warnings, treat warnings as errors) | `OFF` |
| `USE_CLANG_TIDY` | Use `clang-tidy` during build | `OFF` |
The `BUILD_DOCS` (public API documentation) and `BUILD_INTERNAL_DOCS` (public and private API documentation) require [Doxygen](https://www.doxygen.nl/)
and, optionally, `dot` (a part of [Graphviz](https://graphviz.org/)).
The `USE_CLANG_TIDY` option requires [`clang-tidy`](https://clang.llvm.org/extra/clang-tidy/).
#### Build Types
| Build Type | Description |
|------------------|---------------------------------------------------------------------------------|
| `Debug` | Build with debugging information and no optimization. |
| `Release` | Build with optimization for maximum performance and no debugging information. |
| `RelWithDebInfo` | Build with optimization and include debugging information. |
| `MinSizeRel` | Build with optimization for minimum size. |
| `ASAN` | Build with AddressSanitizer enabled for detecting memory errors. |
| `LSAN` | Build with LeakSanitizer enabled for detecting memory leaks. |
| `UBSAN` | Build with UndefinedBehaviorSanitizer enabled for detecting undefined behavior. |
| `TSAN` | Build with ThreadSanitizer enabled for detecting data races. |
| `Coverage` | Build with code coverage analysis enabled. |
ASAN, LSAN, UBSAN, TSAN, and Coverage builds are only supported with GCC or clang.
Coverage build requires `gcov` (GCC) or `llvm-gcov` (clang) and [`gcovr`](https://gcovr.com/en/stable/).
### Running Tests
To run the tests, use the following command:
```sh
ctest -T test --test-dir build/test
```
or run the following binary:
```sh
./build/test/coro_test
```
The test binary uses [Google Test](http://google.github.io/googletest/) library.
Its behavior [can be controlled](http://google.github.io/googletest/advanced.html#running-test-programs-advanced-options)
via environment variables and/or command line flags.
Run `coro_test --help` for the list of available options.
## Usage Examples
The documentation is available at [https://sjinks.github.io/coro-cpp/](https://sjinks.github.io/coro-cpp/).
### Eager Coroutine (`eager_task`)
An *eager coroutine* (or *hot-start coroutine*) starts execution immediately upon creation and keeps running until the first suspending `co_await`.
```cpp
#include
wwa::coro::eager_task my_task()
{
co_await some_other_coroutine();
}
```
See [examples/eager_task.cpp](https://github.com/sjinks/coro-cpp/blob/master/examples/eager_task.cpp)
Eager coroutines can `co_await` other coroutines but they cannot be `co_await`'ed.
### Task (`task`)
*Tasks* are lightweight coroutines that start executing when they are awaited; they can optionally return values (the type of the return value
is determined by the template parameter `T`). Use tasks to create your coroutines, and use `co_await` or `co_yield` within tasks
to perform asynchronous operations.
```cpp
#include
wwa::coro::task task1()
{
co_return 123;
}
wwa::coro::task task2()
{
co_return 456;
}
wwa::coro::task sum()
{
const auto a = co_await task1();
const auto b = co_await task2();
co_return a + b;
}
wwa::coro::task<> print()
{
std::cout << "The result is " << co_await sum() << "\n";
}
```
It is possible to turn any task (or any awaitable) into a fire-and-forget eager coroutine. For the example above,
```cpp
#include
#include
wwa::coro::run_awaitable(print);
```
See [examples/task.cpp](https://github.com/sjinks/coro-cpp/blob/master/examples/task.cpp).
### Generators (`generator`)
*Generators* are special coroutines that produce sequences of values of a specified type (`T`). These values are produced lazily and *synchronously*.
The coroutine body is able to yield values of type `T` using the `co_yield` keyword.
However, the coroutine body is not able to use the `co_await` keyword; values **must** be produced synchronously.
Generators can be used with range-based `for` loops and ranges.
```cpp
#include
wwa::coro::generator fibonacci(int n)
{
int a = 0;
int b = 1;
if (n > 0) {
co_yield a;
}
if (n > 1) {
co_yield b;
}
for (int i = 2; i < n; ++i) {
auto s = a + b;
co_yield s;
a = b;
b = s;
}
}
// ...
std::cout << "The first 10 Fibonacci numbers are: ";
for (auto n : fibonacci(10)) {
std::cout << n << ' ';
}
```
See [examples/generator.cpp](https://github.com/sjinks/coro-cpp/blob/master/examples/generator.cpp).
### Asynchronous Generators (`async_generator`)
*Generators* are special coroutines that produce sequences of values of a specified type (`T`). These values are produced lazily and *asynchronously*.
Unlike the synchronous counterpart, the coroutine body is able to use both `co_await` and `co_yield` expressions.
```cpp
// Simulates an asynchronous task
wwa::coro::task get_next_value(int n)
{
co_return n + 1;
}
wwa::coro::async_generator async_first_n(int n)
{
int v = 0;
while (v < n) {
co_yield v;
v = co_await get_next_value(v);
}
}
// ...
auto gen = async_first_n(5);
auto it = co_await gen.begin(); // IMPORTANT! co_await is required
auto end = gen.end();
while (it != end) {
std::cout << *it << "\n";
co_await ++it; // IMPORTANT! co_await is required
}
```
See [examples/async_generator.cpp](https://github.com/sjinks/coro-cpp/blob/master/examples/async_generator.cpp).
Unfortunately, it is impossible to use asynchronous iterators directly in range-based `for` loops.