In a nutshell, C++ coroutines are almost like regular functions, except that they can be "paused" (suspended), and their state is stored on the heap so they can be resumed later. When you resume a coroutine, its state is loaded back, and execution continues from where it left off.
The complicated part comes from the interfaces through which you use coroutines in C++. Each coroutine needs to be associated with a promise object, which defines how the coroutine behaves (for example, what happens when you co_return a value). Then, there are awaiters, which define what happens when you co_await them. For example, C++ provides a built-in awaiter called suspend_always{}, which you can co_await to pause the coroutine.
If you take your time and go thoroughly through the blog and Cppreference, you'll definitely get the hang of it.
Hope this helps.
[1] https://lewissbaker.github.io/ [2] https://en.cppreference.com/w/cpp/language/coroutines
They are a function that can remember where they are in their own execution so when they are called later they continue execution where they left of.
There are many many ways of implementing that functionality, C++ standard coroutines are only one such implementation.
What you do with them is whatever you want, it's pretty common to handle IO using them but generators are also a pretty common example. But that is generally high level.
C++ coroutines are basic building blocks and are very low level, there is no executor ( rust tokio / python asyncio ) so don't be worried if it seems hard to use, it is hard to use.
Look at std::generator for how coroutines are used to implement a generator, cppcoro is also a pretty popular library that builds abstractions on top of coroutines and also has some executors if I remember correctly.
Their design has a common source, and the magic methods for awaitables as well.
- Heap allocated call frame. Instead of being pushed onto the stack, co-routines tend to have their call frame (local variables, arguments, etc.) placed into heap memory (or at least may be place-able into heap memory). This often enables the other features.
- Control can leave co-routines in more ways than standard function calls. Generally this means returning (often called "yield") to the caller without completing the whole function. It can then be later resumed, returning to where the function originally left off. Generators are a common pattern enabled by co-routines that rely on only this part (and so many systems can optimize out the heap usage, for example).
- A co-routine is usually an object with an interface that allows you to move it around and resume it in different places than it was originally called. This can include on different threads, or depending on the sophistication of the system, different processes or machines.
Those are the three big points in my mind. I'd recommend trying lua coroutines, personally (I like minmalist engines like defold to use it in) to really get a feel for how these are on the edge between "language feature" and "library feature".
you can even try to run those virtual threads on real threads. much fun to be had.
[1] https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous...
> they are functions that can suspend themselves, meaning they stop themselves without returning, even in the middle of their body and then can later can be resumed, continuing execution at the point they suspended from earlier.
If you want to use coroutines in C++ specifically you can have a look at this tutorial, if you want: https://theshoemaker.de/posts/yet-another-cpp-coroutine-tuto... I don't know of anyone that read it, but I spent a lot of time on it.
It essentially tries to explain how to build a coroutine support library yourself, but if you don't care about that, skip it and just use libcoro or cppcoro. They have examples too. My little async io library has some examples as well if you want to get an idea.
Notice that this does not necessarily involve parallelism, although it can. For example, Lua has non parallel (cooperative) co-routines. Go had parallel coroutins, called goroutines, but theoretically only if they they use channels to exchange values. Otherwise, if they’re not exchanging information they would not becoroutins in the sense that they work together in solving something.
This video is the best intro I've found. It actually explains what is happening in memory, which is the only way to really understand anything in C++.
Also this is decent:
https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html
But don't try and write a coroutine library yourself. Use something like libcoro.
Can anyone recommend a good tutorial or resource for me to read.
I find it so frustrating as I don't think it's necessarily a complex subject but my brain just doesn't get it.
Related perhaps but many (many, many) years ago, when learning BASIC, I assumed GOSUB went off and started executing the code in the subroutine as well as the rest of the inline code. That suggests to me that I should perhaps have a deeper understanding of this but I really don't...