Live data from Hacker News

My tutorial and take on C++20 coroutines

scs.stanford.edu

41–50 of 144 posts

Re: My tutorial and take on C++20 coroutines

#41
post #19

Some 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…

To clarify what stackless means when you're writing code: * calling into the coroutine from the caller uses the caller's stack (i.e. it just pushes on another stack frame). * the lack of a coroutine stack ("stackful" coroutine) means that the coroutine can only "yield" a value from a single method; it cannot call a function F, and then have F yield back to the original coroutine's caller. * In other words: you can th…

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.

Re: My tutorial and take on C++20 coroutines

#42
post #27

Earlier quoted context omitted.

C++ coroutines as they are in the standard are not really intended to the everyday programmer (1), but rather for library implementers who will use and abstract them into higher level features. 1: like most of C++ one would way

Ultimately, I think the simultaneous complexity and lack of built in functionality will limit how often people end up using this new part of the standard. I can use coroutines to write high performance, parallel code in other languages without being a mythical ~library implementor~. I usually even write libraries in those languages.

Agreed. I think the killer use is for event-driven programs that already do a lot of "stack ripping". If you are already manually putting all your "local" variables into an object and slicing your functions into many methods, then the grossness of C++20 coroutines will be easy to digest given how much more readable they can make your code.

Re: My tutorial and take on C++20 coroutines

#43
post #19

Some 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 a basis and you can build a library on top of it. It is possible to build single-threaded async code (python or JS style) as well as multi-threaded async code (C# style where a coroutine can end up on a different context), right?

Is there anything like an event loop / Twisted's `reactor` already available yet?

I'm really looking forward to rewrite some Qt callback hell code into async...

Re: My tutorial and take on C++20 coroutines

#44
post #12
post #4

I'm a bit confused by all of this. The idea of coroutines existed since the 60's. Then we came up with OOP that tries to combine function + data. And now we abolished this, are back at only functions and are now solving the 'state capture' problem for functions again with: Coroutines? How does the history of programming paradigms/patterns make any sense? :) Good article though.

Your analysis of history there is a bit lacking. Coroutines didn't go out of fashion because of OOP, it went out of fashion because of structured programming and higher level languages. Coroutines are doable if you're programming directly in assembly, but if you want to do it in C-like structured languages, it turns out that it's really tricky: the whole concept of those languages are about having a single stack and…

> Basically the only languages with full stackful coroutine support in wide use are Lua and (sort-of) Go.

Aren't Javascript Generators also coroutines?

Re: My tutorial and take on C++20 coroutines

#45
post #36

void main() does not exactly inspire confidence. https://stackoverflow.com/questions/636829/difference-betwee...

Umm... if you download the code you will see that main returns int, but the main1...main6 functions invoked by main return void because they don't need to return a value.

I copy and pasted `void main()` from the article. If you read the SO question I linked, you would see that `void` is not a valid return type for `main`.

Re: My tutorial and take on C++20 coroutines

#46

From the existing explanations, I’ve deduced that coroutines are a form of continuation based coopetative multitasking. Is it possible to “restart” the sequence, and how would this be done?

As far as I understand, they are one-shot continuations, so no, they are not restartable unfortunately. I believe at some point one proposal had copyable (and hence multi-shot) continuations, but the proposal had other issues.

Thanks for the quality response. Without restarts, it limits the use cases.

Re: My tutorial and take on C++20 coroutines

#47
post #18

Part 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…

What exactly do you find complex? I see that it is just a blob of C++ that is difficult to read at a first glance, but if you get past that, it is actually super simple.

Re: My tutorial and take on C++20 coroutines

#48
post #18

Part 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…

This is all down to not having a "managed runtime", so it has to be shoehorned into the environment available where you have a stack, a heap, and that's basically it.

Re: My tutorial and take on C++20 coroutines

#49
post #19

Some 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…

Stackless means when you resume a coroutine you're still using the same OS thread stack. Coroutine contexts/activation records are conceptually heap allocated (although in some cases that can be optimized away).

You can use coroutines for what you say, but there are no execution contexts (like a thread pool or an event loop) in C++20 standard library in which to execute coroutines, so to do networking and async I/O you need to use a library or DIY. Hopefully standard execution will come later as part of the Executors proposal.

You can currently use C++ native coroutines with the ASIO library, but this is probably subject to quite a bit of API churn in the future:

https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/ov...

You can also wrap things like ASIO yourself. I did this in 2018 when I was learning about C++ coroutines to create a simple telnet based chatroom:

https://github.com/heavenlake/coro-chat/blob/master/chat.cpp

Note that this code is likely garbage by todays standards. One thing i can't remember is why i needed to use the cppcoro library in the end.

Re: My tutorial and take on C++20 coroutines

#50
post #49

Earlier quoted context omitted.

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…

Stackless means when you resume a coroutine you're still using the same OS thread stack. Coroutine contexts/activation records are conceptually heap allocated (although in some cases that can be optimized away). You can use coroutines for what you say, but there are no execution contexts (like a thread pool or an event loop) in C++20 standard library in which to execute coroutines, so to do networking and async I/O y…

> Stackless means when you resume a coroutine you're still using the same OS thread stack

This is confusing, because it begs the question "same as what?" In fact, you can migrate a coroutine across threads, or even create a thread specifically for the purposes of resuming a coroutine.

But I suppose it is true that from the point at which you resume a coroutine to the point at which it suspends itself, it will use a particular stack for any function calls made within the coroutine. That's why you can't suspend a coroutine from within a function call, because the function call will use the stack.

Post reply on HN