As a developer, when you decide to use bleeding edge C++?? extensions keep in mind that when you do so you're making it much, much harder to run any of your code on a distro more than 4 years old. Is it worth it?
My tutorial and take on C++20 coroutines
101–110 of 144 posts
Re: My tutorial and take on C++20 coroutines
#102Earlier quoted context omitted.
> And from what I've read they are better than Rusts coroutines for this use case Reference please? In what sense are they better, and what makes them better?
Rust's Futures don't have asynchronous destructors (I don't know if coroutines do). When a Future is aborted early, it's destroyed immediately with no remorse. This means it can't simply offer its own buffers to the kernel, because the kernel could write back after the Future has been freed. An API contract that includes "just be careful not to do the stupid thing" is not good enough by Rust's standards, so the only…
Re: My tutorial and take on C++20 coroutines
#103Earlier quoted context omitted.
> And from what I've read they are better than Rusts coroutines for this use case Reference please? In what sense are they better, and what makes them better?
Rust's Futures don't have asynchronous destructors (I don't know if coroutines do). When a Future is aborted early, it's destroyed immediately with no remorse. This means it can't simply offer its own buffers to the kernel, because the kernel could write back after the Future has been freed. An API contract that includes "just be careful not to do the stupid thing" is not good enough by Rust's standards, so the only…
Re: My tutorial and take on C++20 coroutines
#104Some notes: - C++20 coroutines are stackless , meaning that multiple coroutines will share a single OS thread stack. This is non-obvious when you first look in to them them because coroutines look just like functions. The compiler does all the work of ensuring your local variables are captured and allocated as part of the coroutine yield context, but these yield contexts are not stack frames . Every coroutine you inv…
I'm trying to wrap my head around what the implication of stackless coroutines is. Can I use them like `yield` in Python+Twisted, i.e. to implement single-threaded, cooperative parallelism? It would not expect to be able to plainly call a function with a regular call, and have that function `yield` for me - but can I await a function, which awaits another, and so on? As far as I understand, C++20 coroutines are just…
Re: My tutorial and take on C++20 coroutines
#105What I find interesting about coroutines is that they are relatively trivially achieved in hand-coded assembly using `jmp` and being careful about registers in a way that makes sense. `jmp`ing around is a pretty normal way to code in assembly. In a sophisticated environment they become a beast. It surprises me that we actually do lose something meaningful when abstracting to higher level languages.
Re: My tutorial and take on C++20 coroutines
#106Earlier quoted context omitted.
Doing things right takes time. This is one step in making co-routines useful. Library authors now have something to work with to figure out what the next part of the C++ standard is. Expect standard library support in C++ in either c++ 23 or 26.
I agree with GP, I feel a similar disdain when I hear that features that are merged into the standard are for the "library implementors". Boost.Coroutine and Boost.Asio have been around for how long? At least a decade? I think there has been more than enough experience with coroutines and async programming to get coroutines in standard C++ done so that at least the "average" C++ programmer can grok them.
Re: My tutorial and take on C++20 coroutines
#107Earlier quoted context omitted.
You are right that you are effectively stuck in a single coroutine (non-stack) frame. But you can chain multiple such coroutine frames, because one coroutine can co_await another.
Also, if it’s not clear, within a coroutine, you can call any function (or coroutine) you want. It’s just that to co_yield, you have to be in the coroutine not deep in the stack.
Why is this a big deal in C++? Am I missing anything that makes c++ coroutines less powerful than other mainstream solutions? Or are people comparing its power with e.g. Lisp or go?
Re: My tutorial and take on C++20 coroutines
#108Part of this is that I’m tired, but it blows my mind how difficult C++ coroutines are as someone who considers themselves decent at C++ (although maybe I’m not) and uses coroutines in other languages. The amount of code needed to do almost nothing is extraordinary, and putting it all together doesn’t seem like you would often get on your first try. I get that new keywords basically can’t be added, but man, that’s pai…
I just sat down with the tutorial and banged out what I consider to be a holy grail for quality of life. Alas, it's about 50 lines of ugly boilerplate. I'm omitting it, in part because it's untested, but mostly because I'm embarrassed to share that much C++ in a public forum. If anybody close to the C++ standards is listening... please make this boilerplate not suck so hard.
template
struct CoroutineIterator {
left for the reader!
hint: start with the ReturnObject5 struct in the article,
and add standard iterator boilerplate
}
//! yields the integers 0 through `stop`, except for `skip`
CoroutineIterator skip_iter(int skip, int stop) {
for(int i=0;iRe: My tutorial and take on C++20 coroutines
#109Its insane how complex C++20 coroutines are when compared with Rust coroutines.
Re: My tutorial and take on C++20 coroutines
#110Earlier quoted context omitted.
Also, if it’s not clear, within a coroutine, you can call any function (or coroutine) you want. It’s just that to co_yield, you have to be in the coroutine not deep in the stack.
Isn't it like that is most languages? I'm thinking about Python, C#, JS. If you call a blocking function from an async function, you cannot yield from deep inside the blocking function. Why is this a big deal in C++? Am I missing anything that makes c++ coroutines less powerful than other mainstream solutions? Or are people comparing its power with e.g. Lisp or go?
It is sort of a big deal because the discussion of wether adding stackful or stackless coroutines in C++ was an interminable and very visible one. Stackless won.