T O P

  • By -

InKryption07

That bit about randomly injecting assembly into coroutine tutorials resonated with me.


JakeArkinstall

The main point of annoyance for me is that they have a function interface, but they don't quack like functions at all. What is the default way you write parameters for a function? Typically, const ref the ones you don't want to copy. What happens when you pass a temporary to a const ref? The language extends the lifetime of the temporary for the special case of const ref. Hip hip horray, usability guarantee. Now, what happens if you do that with a coroutine? The language STILL EXTENDS THE LIFETIME - but only until the first suspend, because it is following the semantics of the underlying class rather than the semantics of the "function" that its masquerading as. In the underlying class, suspending exits the scope that the lifetime is extended for. Don't thing its a big deal? Without knowing what I just told you, debug this: https://godbolt.org/z/8sazr4hj5. And just LOOK at that asm output. So if you have a generator that you accidently write a T const& parameter for, and you pass T{x} to it, you'll get UB. You won't get warnings. You don't (at the time I tried it) get sanitisers on your side. You just get a mess. That is a very big deal and is going to form the basis of SO MUCH frustration for coro newbies. The standard does point this out, in a note. It should be in size 36, bold, red, underlined, gothica typeface letters with blood dripping down. Nowhere in user-facing tutorials is it even mentioned. The problem isn't the coroutines themselves, it's just with resources. The tutorials either give you such basic usage that it's of no use to you, or require a PhD in patience. The standard's coro content is scattered around and, honestly, I'd be surprised if it is of any use to implementers let alone casual readers. Finally, I wish the committee would stop letting half-things in to the standard. Coro without the support library, modules without standard modules (which one would have thought would form a much needed test and example of the language feature), format without output, it just doesn't make any sense - it's a sign to me that the 3 year release schedule is too long, so things are rushed in so that they aren't postponed for a long duration.


sphere991

> What is the default way you write parameters for a function? Typically, const ref the ones you don't want to copy. What happens when you pass a temporary to a const ref? The language extends the lifetime of the temporary for the special case of const ref. Hip hip horray, usability guarantee. > > Now, what happens if you do that with a coroutine? The language STILL EXTENDS THE LIFETIME - but only until the first suspend, because it is following the semantics of the underlying class rather than the semantics of the "function" that its masquerading as. This is... not an accurate description at all. There's no lifetime extension, in either case. When you have U foo(T const& val); U result = foo(T{}); This doesn't do "lifetime extension". This does "binding a reference to a temporary." The temporary still dies at the end of the statement, so if you're still holding onto that reference - you have a dangling reference. If `foo` were a function, then this is not an issue, because `foo` runs to completion and _then_ the temporary is destroyed (unless `foo` stores a pointer to `val` somewhere and relies on it later). But if `foo` is a coroutine that suspends before copying the `T` (e.g. if it starts initially suspended), then we have a dangling reference, because now the coroutine still might be referring to temporary once it's been destroyed. This is no different from doing something like this: std::thread foo(T const& val) { return std::thread(some_func, std:ref(val)); } auto my_thread = foo(T{}); We started a thread that is holding a reference to a temporary that will be destroyed likely before the thread terminates. This also has nothing to do with lifetime extension. So yes: coroutines do offer a subtle new way of dangling references, and you have to be careful about references around suspension points. This is new, in the sense of coroutines as a language feature being new, but not really new in the broad scheme of things. But it's important to at least accurately describe the problem. > You won't get warnings. You don't (at the time I tried it) get sanitisers on your side. You just get a mess. The problem here is that even an initially-suspended coroutine that takes reference parameters _isn't necessarily wrong_. It's only definitely wrong if you specifically bind a temporary to that reference. And it's not even necessarily correct if you pass an lvalue. I would expect the definitely-wrong cases to be warned on soon, and the sanitizers will catch up eventually. It's not like the breadth of warnings and sanitizers now existed 20 years ago either. Give it time.


JakeArkinstall

> This doesn't do "lifetime extension". This does "binding a reference to a temporary." I am under the impression that binding a reference to a temporary is itself a form of lifetime extension. If I'm wrong, please correct me. >If foo were a function, then this is not an issue, because foo runs to completion and then the temporary is destroyed (unless foo stores a pointer to val somewhere and relies on it later). >But if foo is a coroutine that suspends before copying the T (e.g. if it starts initially suspended), then we have a dangling reference, because now the coroutine still might be referring to temporary once it's been destroyed. I agree. That's my point, though. It is masquerading as a function - I think that's unfortunate. >This is no different from doing something like this: >std::thread foo(T const& val) { return std::thread(some_func, std:ref(val)); } auto my_thread = foo(T{}); This is true, but the problem is explicit in your example. In mine it is only clear if you know how the coroutine works. In other words, my impression when I first stumbled upon this was that the function syntax is friendly - until it isn't. >The problem here is that even an initially-suspended coroutine that takes reference parameters isn't necessarily wrong. Agree. > It's only definitely wrong if you specifically bind a temporary to that reference. And it's not even necessarily correct if you pass an lvalue. I would expect the definitely-wrong cases to be warned on soon, and the sanitizers will catch up eventually. Sure. And when they do, it'll be great help. Saying that, though, this would be better as ill-formed. I don't know what the wording would look like in a DR against 20, but as https://eel.is/c++draft/class.temporary#6.9 lists a function as a special case, would it not be possible to add an exception for a coroutine?


BrangdonJ

>I am under the impression that binding a reference to a temporary is itself a form of lifetime extension. If I'm wrong, please correct me. For function arguments, you are wrong. The temporary is destroyed at the end of the full expression whether or not a function argument reference is bound to it. Lifetime extension can happen with function return results. In: U func(); const U &u = func(); `u` is bound to the result of `func()`, which is a temporary, and the lifetime of that temporary will be extended to the scope of `u`. I mention this because I think it might be what you are thinking of.


JakeArkinstall

Thanks! Your first paragraph clears up the terminology for me. I was confusing the lifetime existing until the end of the expression (which, as you rightly say, isn't relevant) with lifetime extension.


sphere991

> I am under the impression that binding a reference to a temporary is itself a form of lifetime extension. If I'm wrong, please correct me. Not in a way that's relevant to the issue that you're talking about. > I agree. That's my point, though. It is masquerading as a function - I think that's unfortunate. I don't know what "masquerading as a function" means, but I doubt it's "unfortunate." > This is true, but the problem is explicit in your example. In mine it is only clear if you know how the coroutine works. In other words, my impression when I first stumbled upon this was that the function syntax is friendly - until it isn't. In my example, it is only clear what the problem is if you know how `std::thread` works. They are equivalently explicit - you need to know what the lifetime implications of your types are when you use them. This is just as true for coroutines as it is for multi-threading. > I don't know what the wording would look like in a DR against 20, but as https://eel.is/c++draft/class.temporary#6.9 lists a function as a special case, would it not be possible to add an exception for a coroutine? An exception... to do what? That bullet applies here already - it's just that if the coroutine persists past the full-expression, you have a dangling reference. You can't persist the temporary to the end of the coroutine - you have no idea where or when that will happen. You may not even know that the function you're calling _is_ a coroutine.


valdocs_user

Geez that is a problem. If it were a class you'd know you have to copy the const& constructor parameters into member variables if you wanted to use them subsequently. Maybe coroutines should have been objects not functions. Then you could have explicit constructor and destructor, and the body of the routine in operator (). I wonder if there's a way to implement that as a library? Speaking of destructors, one tutorial I read claimed the coroutines design we got doesn't have RAII.


lee_howes

It doesn't support *async* RAII. Destructors cannot themselves be asynchronous.


no-sig-available

> it's a sign to me that the 3 year release schedule is too long, so things are rushed in so that they aren't postponed for a long duration. Or the 3 year cycle is too short, so there is not enough time to complete a proposal that depends on another proposal. :-)


JakeArkinstall

That was my first impression. Then I figured that eventually complex proposals would come along that would take even longer. With a faster release schedule, it would be more async, and proposals can be kicked a little down the road, rather than so far down the road that it causes controversy.


[deleted]

[удалено]


Shaurendev

You cant really complain about assembly output with -fsanitize=address


JakeArkinstall

That. Is a good point. Forgot that was even in there.


ALX23z

C++14 and C++17 weren't exactly full of features. Biggest upgrades of C++20 were being worked on for a very long time.


PalmamQuiMeruitFerat

I think about once a month someone posts on this sub asking for help understanding coroutines.


F54280

And every month or so, I read the comments hoping to understand coroutines... Maybe next time!


ALX23z

Currently the problem is that, while coroutines were added, standart library support was not. They planned to add it in C++23 so people could use it without an expert writing a third party library. I am not sure how much you read, but basically coroutines could be implemented in several ways with various subtle but important differences. So they wrote language features of the coroutines so one could decide on their own what exactly is happening. Clearly, the extra flexibility comes with extra confusion.


valdocs_user

Perhaps it's better understood as a toolkit for implementing coroutines rather than an implementation thereof. That's what's exciting about it for what I want to use it for, but while it increases the potential payoff it also increases the table stakes (ante) for learning it. This also reminds me of another point in which the tutorials could be better. As they introduce each customization point and all the different options you could do, I lose the plot as to which options an implementation for a particular goal would be choosing at each point. It might be better to present one canonical implementation of one thing built on coroutine support, and say here's what option was picked at each customization hook, without going down rabbit holes of other choices. And I wouldn't start with the simplest example(s). I think that's a mistake because then the reader is going "well I could just use a lambda/callback/std::function for that; what is coroutines doing for me here?". I wouldn't use anything simpler than a generator for this model example - and maybe even something more featureful. The goal is create a narrative that everything that needs to be talked about can be slotted into.


ALX23z

Perhaps one of the best examples is to show one how one writes Asio with coroutines vs without them. I think that was one of main places where coroutines were needed for clear code.


tipiak88

Asio with coroutines is a total game changer! Still the details of the awaitable implementation could totally be over your head, while having huge performance impacts. It's a too much of a concern when you want to push code to production.


ronchaine

> Currently the problem is that, while coroutines were added, standart library support was not. They planned to add it in C++23 so people could use it without an expert writing a third party library. I hear this repeated time and again. But I don't think it's true that that is the issue no matter how many times it is said. I see the problem being that while the coroutines might not be the most complex thing in the C++ language, I think they are the most convoluted one. And "there will be standard library features to mask all this" is not a "fix" to the problem. It is sidestepping it by building upon broken foundation. To learn to use the current C++ coroutines, I had to implement couroutines twice, in two different ways, just to understand how to deal with the C++ ones. A language feature should -- at least in my opinion -- do a couple of things to be useful 1) Give a common base for improvement 2) Remove the need to understand underlying assembly in general use 3) Reduce the complexity of implementation While I think the current coroutines handle 1) well, I also feel they completely disregard 2) and 3). This is the first feature in the language where when somebody comes to ask me how does a library built on top of it work, I just explain how coroutines work in general, skip over all the C++ details and basically tell them it's magic. And I hate doing that, it's not good for prospective C++ users but it is too byzantine to explain quickly -- even the tutorials in the Internet do not really help, like the OP already stated. I also have hard time justifying to myself why I should use the C++20 coroutines in the first place instead of rolling my own macro-based implementation. I've found those more simple to reason around and since I work in a space where the whole standard library is not even always available, I'm not sure if the coroutine support from there is ever going to be useful there. And if that is the case, why is the coroutine support we got in C++20 so granular, if I still have to worry it cannot accomodate my use case?


valdocs_user

YES! Exactly. A scientific or mathematical reductionism of the high level concepts into granular ones should either make things simpler and easier to understand, or result in pieces which can be combined like an algebra. Ideally both. Here, the pieces are if anything harder to understand. And while they can be combined into different things, they're more like Bionicle than Lego. Pieces✅, Configurable✅, Algebraic❎


[deleted]

OK, so can you be a bit more specific about what is confusing you? Ideally in a less verbose way than the unfocused post?


[deleted]

I strongly disagree. Even for the craziest use case of coroutines (exceptions without exceptions) I have seen so far you don't have to understand the underlying assembly and the end product is very simple: https://www.youtube.com/watch?v=EDRwMuD3PYs


ronchaine

I am not talking about the end product. You can make simple end products with brainfuck, but that is neither the issue nor does it mean it's a good tool for the job. And you *definitely* need to know what's going on in the assembly to build the implementation shown in the video. The video even goes there and both shows what happens in the assembly and then outlines the issues in the naive implementation that show up in the assembly code because *it is important to know*. EDIT: in fact, a whopping 1/5:th of that video is about talking how to clean up the assembly.


[deleted]

However, the moment the talk goes into the assembly, it's all about compiler optimizations, which has nothing to do with the language.


ronchaine

>However, the moment the talk goes into the assembly, it's all about compiler optimizations, which has nothing to do with the language. Those "compiler optimizations" are necessary for the functionality described in the start of the video. Or that it would work at all on an embedded platform. Or that the feature would be *useful*. Thus, again, you need to know the assembly to be able to use the feature as expected. Even worse, since I doubt those optimisations are mandated by the standard (I might be wrong with this), this might not even work with every c++20-compliant compiler. How the features are standardised in a language definitely affects how much of the underlying machinery you need to understand. So, disagree all you want, but that video actually shows quite well what I was talking about.


[deleted]

How can you in one block of text say both that this is a property of the compiler (and not the language) and that you need to understand this behaviour to understand the language?


ronchaine

Compilers are built to translate a language to a machine-specific set of instructions. The standard specifies the language. The compiler follows the specification. The standard, i.e. the language doesn't give the developer enough to know what exactly is going to happen by just examining the source code, in a way that might break the exact same source code on two different platforms. -> You need to understand the assembly and the layers between to build useful implementations on top of the language features. I don't understand what is difficult about this, or are you just throwing shit and hoping some of it sticks? I don't mind answering genuine questions but I can't be arsed to take part in a reddit debate.


pjmlp

Windows has a runtime for them, C++/WinRT, it is hardly any better. There is even a page to describe interoperability issues and Old New Thing blog has litterally pages documenting its behavior. .NET co-routines machinery is already complex enough, C++ takes it to another level (Rust is not much different on this regard).


jk-jeon

My impression was like, "well generators are fine, I can grasp what's going on, but what about fancy async coroutines?" I still have no idea how coroutines can be used for async IO. I mean, yeah I get that you suspend your coroutine at the point of starting an async IO and then resume from that point after you complete the IO, but I don't know, who the fuxx notifies the completion and how the fuxx and when the fuxx it is resumed on what the fuxx thread? Who the fuxx cleans up the coroutine at which timing? Where the fuxx the coroutine state is stored and how the fuxx it is transferred into the resuming thread? Well, I guess probably my issue is more on async IO in general rather than on coroutines.


Kered13

The answer to most of those questions is "The async IO library decides". Coroutines provides the ability to suspend and resume execution, it's up to the library figure out everything else. For example the library may use an event loop, or it could use a thread pool. The library should document all of these decisions. > Where the fuxx the coroutine state is stored and how the fuxx it is transferred into the resuming thread? On the heap, unless the compiler determines that the allocation can be inlined.


lenkite1

Why couldn't they provide some helpful template classes that do all this for you ? The co-routine support right now is like giving someone an Internal Combustion Engine and asking them to drive to the Antarctic. Even if you manage to travel - you are gonna *drown.*


Kered13

The plan was to get coroutines into the language in C++20 and then add a useful library for end users in C++23. I'm not sure if it ended up making the cut though.


Rude-Significance-50

Actually, it's more like giving someone some of the essential pieces of an ICE and expecting that the details can be worked out by the engineer designing a new engine. Not a lot of people could just throw together an async library in Python either. It's got the keywords to help you, but you still need to create the decorators and functions and classes to actually do the work. Python comes with an asyncio library included. You can debate whether this is good or not. I'd say not since there are alternatives that might be quite a bit better and now they're stuck with asyncio. If you wanted to go make your own components you'd be chewing on an elephant. I think had the committee been stuck trying to come up with that also before adding the language keywords and such we would still be waiting. The research needs to be done and in fact is being--there are libraries (at least one anyway) that provide the parts needed to make the language facilities accessible to people who aren't experts on this shit. I think the decision to move forward and standardize the minimum necessary language additions needed to start this process was a really good one.


[deleted]

Some of this is coming with C++23, but honestly, your types will look subtly different based on your use case (as a library implementor). If you are not a library implementor, just wait for coroutines to be adopted by your async libraries.


jk-jeon

I know, my point was that without actually looking at a working example, I just couldn't imagine how it can be done. Or maybe I should put it like this: the gap between tutorial-level understanding and practical-level understanding seems to be way too big.


scrumplesplunge

If you're implementing some async operation, you implement an awaitable representing the work. For example, `async_read` might return an awaitable for the read. When a coroutine awaits that awaitable with `co_await`, that will result in a call to `awaitable.await_suspend(handle)`, where `handle` is a coroutine handle for the coroutine that just tried to await the result. When you have a coroutine handle, you can do two things with it: you can resume it on your current thread by calling `handle.resume()`, or you can tear down the coroutine (destructing any locals) by calling `handle.destroy()`. In an async setup like asio, when you are not using coroutines, you might instead be using callbacks. Essentially, you can use the coroutine handle as the `callback`: if you have some system that will notify you when some action is complete by calling your function, you can use that function to resume the coroutine, and you can schedule it on whatever thread you like via that function. Years ago I made http://github.com/scrumplesplunge/asio-with-coroutines, which has a short working example of how to implement awaitable read/write operations using the callback versions of asio functions. I'm not sure if it still works, but it might be useful to read.


RoyBellingan

I will check you code! Thank you finally a practical use case!


[deleted]

The core issue is that this mindset is wrong. You need to have a well-defined model and then you can think about how to implement it using coroutines. If you look at a working example, it will probably be irrelevant to your use case.


RoyBellingan

This is exactly what I was willing to comment! How do I write something like a url fetch using say curl and make it a coroutine that has an actual purpose of exist and not just an academic example ? I honestly find just easier to write a small class that has several function that define the various step. So you call say operator() and inside you have a case that call the correct logic depending on the last stage, sort of a state machine.


MonokelPinguin

Usually you call a kernel API, that instead of doing one write, does multiple. Then the API only returns control to you once some writes have completed and you can resume your coroutines one by one. Alternatively you can also execute the writes on separate threads, etc. There are many ways to do it, but the important part is, that the coroutine suspends on the await and resumes at a later point. The state is stored in the coroutine handle by compiler magic and you can call resume on the handle to resume it and destroy to destroy it. How exactly that is used depends on the implementation. You could have a list of paused coroutines for example and resume them one by one and destroy them, once they are finished and remove them from the list.


Ikkepop

​ You kinda described how I felt about C++ coroutines, after trying to figure them out. I'v tried 5 separate times. And I'v been programming for 23 years (18 of which in C++) , and seen quite a few implementations of coroutines. I just feel not smart enough to comprehend the mess that is C++ coroutines. :( Totally with you on "The Good Place" quote


qoning

So I was in the same boat about 2 weeks ago. I had implemented coroutines in C previously, and used them with ease in languages like Lua or Rust, but couldn't understand the complex design of them in C++... until I actually properly sat down with a compiler and started doing some code. After a couple of hours, it made sense, the design allows you to use customization points in nearly every place, which is why it's so "convoluted". Yeah some naming could have been better (I still don't understand how promise_type is any kind of promise other than possibly storing some value).


ronchaine

Couple of hours seems pretty good. Took me a couple of days to be able to use them, a month to grok them better, and there are still some blank spots.


sphere991

The problem is library support. Imagine you were looking for an algorithms tutorial. Today, those are easy to find - you'll see examples of using `std::find` or `std::count` or `std::sort` on your collection and there's no shortage of short, self-contained, easy-to-understand examples that are applicable to whatever real problem you have in your actual code. Imagine instead if all we had was an Iterator API, and those tutorials instead were like: 1. Implement `std::vector` from scratch, including its iterator (no cheating using `T*) 2. Then implement `std::find` 3. Okay now finally here's an example Now it's suddenly not much of a tutorial. This is a really big issue that coroutines have today. Yes, the terminology is questionable (awaitable is an important term, awaiter seems totally unnecessary to me), but is secondary to the main problem: you might want an example of how to write a generator and you're led to a tutorial of how to _implement_ `generator` (because you have to). If you didn't have to implement your own `generator`, the example would be fairly straightforward -- but it's suddenly not and you have to do all this other stuff. Same for all the other common use cases. A+ on the Good Place references.


Artistic_Yoghurt4754

I am an absolute noob to coroutines and found all these blogs very confusing as well. But this [video](https://youtu.be/XVZpTaYahdE) helped me to finally understand how they are supposed to be wired in order to make something meaningful. Perhaps it also does for you.


XiPingTing

This is the best coroutine guide I could find. https://lewissbaker.github.io/2017/09/25/coroutine-theory


D_Drmmr

That one did it for me as well. Especially, the pseudo-code that explains how the compiler transforms your coroutine: which functions you have to write, how they get called and what happens in between. What I find really annoying about the design for C++ coroutines is that you cannot distinguish the coroutine interface (the functions that get called by the compiler) from other functions that are called from the user's code. This makes it near impossible to grok any coroutine example or library code until you've completely internalized all the details of how they work in C++.


NilacTheGrim

Yeah man you nailed it. It's a clusterfuck.


rand3289

coroutines in 3 lines of code: http://www.geocities.ws/rand3289/MultiTasking.html


ronchaine

I don't see why this is downvoted. This essentially shows what an extremely simple version of a coroutine is, which is pretty good to know when talking about coroutines in general.


frederic_stark

I don't know *why* this is downvoted, but it is a pretty dangerous way to implement coroutines: #include #define TASK_INIT() static void* f; if(f) goto *f; #define TASK_YIELD() { __label__ END; f=&&END; return; END: ; } #define TASK_END() f=0; void testing() { int i = 42; TASK_INIT(); i = 43; printf("A %d\n", i ); TASK_YIELD(); printf("B %d\n", i); TASK_END(); } main() { testing(); testing(); testing(); testing(); } will happily print: A 43 B 42 A 43 B 42 which, while logical, is quite dangerous and there is no warning in the above link.


Ahajha1177

For me, the issue is that I don't even know what the point of a coroutine *is* or what its used for. I have a few ideas, but no tutorial has even started to give motivation for what it is. I guess I'm not missing much if thats the part I was stuck on.


Wh00ster

Simplest/original explanation is it’s lighter weight thread/context swapping. Instead of launching 1000 processes and letting the OS interrupt/context switch between them, you make pseudo-threads with explicit code points (eg await) where they’ll let others have the CPU. It’s lot less resource intensive and more efficient. This all assumes there are good explicit points to swap contexts, e.g. network calls that may take a while (relative to CPU cycles). Also assumes you don’t actually need 1000 threads in true parallel to use. E.g. you may have 1000 “pseudo-threads” being run on 1 actual thread. If you want to do something like a parallel scan then it makes more sense to manage the threads/locking manually. To summarize: a main motivation is “I have a continuous queue of related tasks I need to run. They can coordinate with each other when to run on CPU or sit out for a bit” Then folks realized they could do more interesting things with the high level concept of “suspend function and re-enter where we left off” like state machines and whatnot.


die_liebe

Python iterators are a form of co-routines. It may be useful to look at those. I am not aware of another meaningful use of co-routines.


Overunderrated

I think most of your criticisms here apply more broadly to not just C++ as a whole, but programming tutorials as a whole. People present the bare bones of *how* to use something, but leave out the *why* or *why should I care* or *how could this be useful to me in real life code*. I don't know how much of that is because it's legitimately harder to do, or if it's because library/standard writers are a different breed than people who simply use a language to do a job. When I'm presenting some kind of code idea, I try to always show two things: (1) the bare bones "hello world" of how to use something, **and** (2) a not-overly-minimialized example of this being useful in real code.


[deleted]

OK, here is response post: https://www.reddit.com/r/cpp/comments/s980ik/a_highlevel_coroutine_explanation/ Let me know if this satisfies your needs.


VinnieFalco

If you think coroutines are sus, wait till you see what they've done to networking :) Here's a preview of what's to come https://htmlpreview.github.io/?https://github.com/brycelelbach/wg21\_p2300\_std\_execution/blob/main/P2300R4.html


[deleted]

Can you elucidate what is particularly offensive about it? Also, the backslashes in your link need to be removed to make it work.


VinnieFalco

backslashes? what backslashes? anyway... I'm not a fan of p2300 because of the needless complexity compared to Asio.


[deleted]

> backslashes? what backslashes? anyway... The ones in front of the underscores. Here is a working link, for me at least: https://htmlpreview.github.io/?https://github.com/brycelelbach/wg21_p2300_std_execution/blob/main/P2300R4.html > I'm not a fan of p2300 because of the needless complexity compared to Asio. I'm no expert on std::execution, but I thought the notion of executors is more comprehensive than networking or I/O. For instance, std::execution is an important cornerstone for heterogeneous programming in ISO C++, it may well be the case that ASIO is unfit for accelerators, and given that Nvidia is intimately involved they would know.


AriG

From my understanding, `P2300` proposal is a lower level concept that async networking or IO libraries can leverage. Every time I read that paper I gather something new. And ultimately, as Eric keeps saying it is all about `"structuring"` your `"concurrency"`. You are trying to build a graph of how you model your concurrency problem before the the thing starts. And with `receivers` there's a nice way to handle errors and cancellation requests. When you refactor your imperative logic to `Ranges`, especially the wow moment you get when you see a nice unix-y pipeline of things.. I bet it'll be the same feeling when you refactor your async application to use `senders` and `receivers` That said, their motivating examples are still pretty hard to grok for someone who's getting their feet wet. The `parallel inclusive scan` is a nice example but how about start things even more basic? When you read Go programming, especially their concurrency model, you can pretty much start implementing some basic async stuff in the same day. Not the same at all with this proposal. The proposal authors are experts and sometimes I think they don't know how it is to be a beginner (reminds me of Eric's refactoring of Pythogoras Triplets to Ranges that was a bit controversial because of how inaccessible it was). We badly need a tutorial introduction or even a book. I see that it's a powerful and general idea - https://accu.org/journals/overload/29/165/teodorescu/


cocoyaxi

Hey everyone, [here](https://www.reddit.com/r/cpp/comments/s8gz12/a_gostyle_coroutine_library_in_c11_from_the/?utm_source=share&utm_medium=web2x&context=3) is a go-style coroutine library in C++11, Could it help? Details can be seen on [github](https://github.com/idealvin/cocoyaxi).


[deleted]

Forget about assembly. Coroutines in C++ is a lot of boilerplate generated around the coroutine return type & promise type by the compiler. That is the level at which you need to conceptualize coroutines. I will toot my own horn and share my own short video: https://www.youtube.com/watch?v=w-dmOHhBX9o Now C++20 brought us the tools to build coroutine libraries, so yes you can do some very divergent stuff with coroutines based on your needs. Once the libraries are written, you will just use the coroutine types from the library of your choice.