0
0
CppConceptIntermediate · 3 min read

What Are Coroutines in C++: Simple Explanation and Example

In C++, coroutines are special functions that can pause their execution and resume later, allowing efficient asynchronous or lazy computations. They help write code that looks sequential but runs without blocking, improving performance and readability.
⚙️

How It Works

Think of a coroutine like a bookmark in a book. When you read a chapter, you can put a bookmark to pause and come back later exactly where you left off. Similarly, a coroutine can pause its work at certain points and resume later without starting over.

Under the hood, C++ coroutines transform the function into a state machine that keeps track of where it paused. This lets the program do other tasks while waiting, making it great for things like waiting for data or handling many tasks at once without slowing down.

💻

Example

This example shows a simple coroutine that generates numbers one by one, pausing after each number until the next is requested.

cpp
#include <coroutine>
#include <iostream>
#include <optional>

// A simple generator coroutine that yields integers
struct Generator {
    struct promise_type {
        int current_value;
        std::suspend_always yield_value(int 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 return_void() {}
        void unhandled_exception() { std::exit(1); }
    };

    std::coroutine_handle<promise_type> handle;

    Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
    ~Generator() { if (handle) handle.destroy(); }

    bool next() {
        if (!handle.done()) {
            handle.resume();
        }
        return !handle.done();
    }

    int value() { return handle.promise().current_value; }
};

Generator count_to_three() {
    for (int i = 1; i <= 3; ++i) {
        co_yield i; // pause and yield i
    }
}

int main() {
    auto gen = count_to_three();
    while (gen.next()) {
        std::cout << "Got: " << gen.value() << "\n";
    }
    return 0;
}
Output
Got: 1 Got: 2 Got: 3
🎯

When to Use

Use coroutines when you want to write code that waits for things like data from a file, network, or timer without freezing your program. They are great for asynchronous programming, where many tasks run at once but you want to keep your code simple and easy to read.

For example, coroutines help in games to load resources smoothly, in servers to handle many users at the same time, or in user interfaces to keep them responsive while doing background work.

Key Points

  • Coroutines let functions pause and resume, saving their state.
  • They improve efficiency by avoiding blocking waits.
  • C++ coroutines use special keywords like co_yield, co_await, and co_return.
  • They are useful for asynchronous tasks and lazy computations.

Key Takeaways

Coroutines in C++ allow functions to pause and resume, enabling efficient asynchronous code.
They help write clear, non-blocking code for tasks like waiting on data or handling many operations.
Use co_yield, co_await, and co_return to control coroutine behavior.
Coroutines are ideal for improving performance in networking, UI, and concurrent programming.