Sparks

C++20 Coroutines: a short series

2: Coroutines in C++

Coroutines: Basics, Py->C++20, Benchmarking

This is a mini-series leading up to implementing coroutines in C++20, (“what I learnt on my holiday”). It also has comparison with other implementations, as part of my ModernCPP explorations. (first post)

In C++, much like memory management, building and using coroutines is somewhat more DIY. It also means that the API of using them depends heavily on the specific coroutine implementation you’re using. The examples I use here use the API I’ve written: one 18 1/2 years ago(!) when I was actively working on Kamaelia, one recently.

Simple Generators in C++03

In C++03, given sufficient scaffolding, you can define the fibonacci generator look like this. (There is no standard approach here)

class Fibonnaci: public Generator {
    int a, b, s;
public:
    Fibonnaci() : a(1), b(1), s(0)  { }
    int next() {
    GENERATOR_CODE_START
        while ( true ) {
            YIELD(a);
            s = a + b;
            a = b;
            b = s;
        };
    GENERATOR_CODE_END
    };
};

You can then use this generator like this:

void runFibonnaci() {
    Fibonnaci a;
    for(int j=0; j<22; j++) {
        try {
            std::cout << "Yield in Fibonnaci:"<< j<< " value:" << a.next() << std::endl;
        } catch(StopIteration null){
            std::cout << " Exception Caught" << "...\n";
            break;
        }
    };
}

Simple Generators in C++20

In C++20, given sufficient scaffolding, you can define the fibonacci generator look like this: (There also is no standard API here)

simple_generator<int> fibs(int max) {
    int a{1}, b{1}, n{0};
    for(int i=0; i< max; i++) {
        co_yield a;
        n = a + b;
        a = b;
        b = n;
    }
}

Using this looks like this:

    for(auto i : fibs(20) ) {
        print("FIB : {}\n", i);
    }

Discussion

As noted, the C++03 code is “pretty close” to the python API, but definitely looks a bit odd from a C++ perspective. It’s definitely a bit odd with a few gotchas. It is pretty simple though. The simplicity of the generator implementation has some benefits we’ll see in benchmarking.

However, as noted, the C++20 code looks much closer to the python version, and also is relatively idiomatic C++. It doesn’t rely on any macro magic to work.

Being C++ though, you are required to implement simple_generator AND, the implementation also has to handle all the details of how co_yield is implemented too.

NOTE: In both cases, you cannot know what these two functions do without understanding the specific API used (unlike python and similar, behaviours can be different).

Implementing C++ Coroutines

NEXT POST(s): The next couple of posts cover how to implement C++ coroutines:

Where’s the code?

Update: 11/4/2023

Want to read and runcode, not the blog? I know the feeling. I recently created a github repo specifically for putting code from blog posts in. Though recognising that it might also have non-code stuff added at some point, I’ve called it blog-extras.

The blog-extras repo can be found here:

The code specific to this series of short posts can be found here:

That repo will get fleshed out with code/examples from other posts in this blog too.

Updated: 2023/09/15 21:12:32