Coroutines
Asynchronous programming with cooperative multitasking
🔄 What are Coroutines?
Coroutines enable functions to suspend and resume execution, allowing cooperative multitasking and elegant asynchronous programming without complex callback chains or thread management.
#include <coroutine>
std::generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
std::tie(a, b) = std::make_pair(b, a + b);
}
}
Key Concepts
co_yield
Suspend and return a value
co_yield value;
co_await
Wait for async operations
auto result = co_await async_op();
co_return
Return from coroutine
co_return final_value;
Generators
Lazy sequence production
std::generator<int> numbers();
🔹 Simple Generator
C++20 coroutines enable efficient generator patterns through lazy evaluation and cooperative multitasking. A generator function suspends execution after yielding each value, then resumes on-demand when the next value is requested. This approach is ideal for processing sequences, data streams, and large datasets incrementally without loading entire collections into memory. Coroutines eliminate the need for complex state machines, making asynchronous code more readable and maintainable while improving performance through reduced memory overhead and efficient resource utilization.
#include <iostream>
// Simple generator implementation
template<typename T>
struct Generator {
struct promise_type {
T current_value;
std::suspend_always yield_value(T value) {
current_value = value;
return {};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
void unhandled_exception() {}
};
std::coroutine_handle<promise_type> h;
Generator(std::coroutine_handle<promise_type> handle) : h(handle) {}
~Generator() { if (h) h.destroy(); }
bool next() {
h.resume();
return !h.done();
}
T value() {
return h.promise().current_value;
}
};
Generator<int> counter(int start, int end) {
for (int i = start; i <= end; ++i) {
co_yield i;
}
}
int main() {
auto gen = counter(1, 5);
while (gen.next()) {
std::cout << gen.value() << " ";
}
std::cout << std::endl;
return 0;
}
Output:
1 2 3 4 5
🔹 Async Task Example
C++20 coroutines enable elegant asynchronous programming through the co_await keyword, allowing functions to suspend execution while waiting for operations to complete. Unlike traditional threading approaches that require complex synchronization, coroutines provide lightweight cooperative multitasking by pausing at await points and resuming when results are available. This example demonstrates creating a custom Task type with promise semantics and an Awaiter for simulating async delays. The coroutine suspends during co_await delay(1000), permitting other work to proceed concurrently without thread overhead. This pattern is essential for building scalable, responsive applications with minimal resource consumption and cleaner, more maintainable asynchronous code.
#include <iostream>
#include <thread>
#include <chrono>
template<typename T>
struct Task {
struct promise_type {
T result;
Task get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_value(T value) {
result = value;
}
void unhandled_exception() {}
};
std::coroutine_handle<promise_type> h;
Task(std::coroutine_handle<promise_type> handle) : h(handle) {}
T get() {
return h.promise().result;
}
};
struct Awaiter {
int delay_ms;
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<> h) {
std::thread([h, this]() {
std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
h.resume();
}).detach();
}
void await_resume() {}
};
Awaiter delay(int ms) {
return Awaiter{ms};
}
Task<int> async_computation() {
std::cout << "Starting computation...\n";
co_await delay(1000); // Simulate async work
std::cout << "Computation complete!\n";
co_return 42;
}
int main() {
auto task = async_computation();
// Do other work while computation runs
std::cout << "Doing other work...\n";
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
std::cout << "Result: " << task.get() << std::endl;
return 0;
}
Output:
Starting computation... Doing other work... Computation complete! Result: 42
🔹 Fibonacci Generator
Infinite sequences like Fibonacci are elegantly implemented with coroutine generators. Each call resumes the coroutine to compute the next value, supporting unbounded iteration without precomputation. This pattern is memory‑efficient and directly expresses mathematical sequences in code.
#include <coroutine>
#include <iostream>
template<typename T>
struct InfiniteGenerator {
struct promise_type {
T current_value;
std::suspend_always yield_value(T value) {
current_value = value;
return {};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
InfiniteGenerator get_return_object() {
return InfiniteGenerator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
void unhandled_exception() {}
void return_void() {}
};
std::coroutine_handle<promise_type> h;
InfiniteGenerator(std::coroutine_handle<promise_type> handle) : h(handle) {}
~InfiniteGenerator() { if (h) h.destroy(); }
T next() {
h.resume();
return h.promise().current_value;
}
};
InfiniteGenerator<long long> fibonacci() {
long long a = 0, b = 1;
while (true) {
co_yield a;
auto temp = a + b;
a = b;
b = temp;
}
}
int main() {
auto fib = fibonacci();
std::cout << "First 10 Fibonacci numbers:\n";
for (int i = 0; i < 10; ++i) {
std::cout << fib.next() << " ";
}
std::cout << std::endl;
return 0;
}
Output:
First 10 Fibonacci numbers: 0 1 1 2 3 5 8 13 21 34