News for hackers. Stuff that matters.
Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Wednesday March 25, 2026 @08:17AM (#47516338)
Coroutines is just a way to write continuations in an imperative style and with more overhead.

I never understood the value. Just use lambdas/callbacks.

Re:Looking at Unity made me understand the point of C++ coro... by usrnm on Wednesday March 25, 2026 @08:57AM (#47516738)
> Just use lambdas/callbacks

"Just" is doing a lot of work there. I've use callback-based async frameworks in C++ in the past, and it turns into pure hell very fast. Async programming is, basically, state machines all the way down, and doing it explicitly is not nice. And trying to debug the damn thing is a miserable experience

Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Wednesday March 25, 2026 @09:00AM (#47516779)
You can embed the state in your lambda context, it really isn't as difficult as what people claim.

The author just chose to write it as a state machine, but you don't have to. Write it in whatever style helps you reach correctness.

Re:Looking at Unity made me understand the point of C++ coro... by Sharlin on Wednesday March 25, 2026 @09:13AM (#47516922)
You still need the state and the dispatcher, even if the former is a little more hidden in the implicit closure type.
Re:Looking at Unity made me understand the point of C++ coro... by affenape on Wednesday March 25, 2026 @08:49AM (#47516649)
Not necessarily. A coroutine encapsulates the entire state machine, which might pe a PITA to implement otherwise. Say, if I have a stateful network connection, that requires initialization and periodic encryption secret renewal, a coroutine implementation would be much slimmer than that of a state machine with explicit states.
Re:Looking at Unity made me understand the point of C++ coro... by jayd16 on Wednesday March 25, 2026 @10:14AM (#47517636)
You can structure coroutines with a context so the runtime has an idea when it can drop them or cancel them. Really nice if you have things like game objects with their own lifecycles.

For simple callback hell, not so much.

Re:Looking at Unity made me understand the point of C++ coro... by spacechild1 on Wednesday March 25, 2026 @09:37AM (#47517189)
> Just use lambdas/callbacks.

Lol, no thanks. People are using coroutines exactly to avoid callback hell. I have rewritten my own C++ ASIO networking code from callback to coroutines (asio::awaitable) and the difference is night and day!

Re:Looking at Unity made me understand the point of C++ coro... by duped on Wednesday March 25, 2026 @12:00PM (#47519197)
The value is fewer indirect function calls heap allocations (so less overhead than callbacks) and well defined tasks that you can select/join/cancel.
Re:Looking at Unity made me understand the point of C++ coro... by Sharlin on Wednesday March 25, 2026 @09:12AM (#47516909)
Did you read the article? As the author says, it becomes a state machine hell very quickly beyond very simple examples.
Re:Looking at Unity made me understand the point of C++ coro... by kccqzy on Wednesday March 25, 2026 @09:32AM (#47517131)
I just don’t agree that it always becomes a state machine hell. I even did this in C++03 code before lambdas. And honestly, because it was easy to write careless spaghetti code, it required a lot more upfront thought into code organization than just creating lambdas willy-nilly. The resulting code is verbose, but then again C++ itself is a fairly verbose language.
Re:Looking at Unity made me understand the point of C++ coro... by DonHopkins on Wednesday March 25, 2026 @09:29AM (#47517094)
The Unity editor does not let you examine the state hidden in your closures or coroutines. (And the Mono debugger is a steaming pile of shit.)

Just put your state in visible instance variables of your objects, and then you will actually be able to see and even edit what state your program is in. Stop doing things that make debugging difficult and frustratingly opaque.

Re:Looking at Unity made me understand the point of C++ coro... by jayd16 on Wednesday March 25, 2026 @10:39AM (#47517948)
Use Rider or Visual Studio. Debugging coroutines should be easy. You just can't step over any yield points so you need to break after execution is resumed. It's mildly tedious but far from impossible.
Re:Looking at Unity made me understand the point of C++ coro... by socalgal2 on Wednesday March 25, 2026 @01:26PM (#47520458)
I'll take the bait. Here's a coroutine

    waitFrames(5); // wait 5 frames
    fireProjectile();
    waitFrames(15);
    turnLeft(-30/*deg*/, 120); // turn left over 120 frames
    waitFrames(10);
    fireProjectile();
    // spin and shoot
    for (i of range(0, 360, 60)) {
      turnRight(60, 90);  // turn 60 degrees over 90 frames
      fireProjectile();
    }
10 lines and I get behavior over time. What would your non-coroutine solution look like?
Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Wednesday March 25, 2026 @01:47PM (#47520774)
Given a coroutine body

``` int f() { a; co_yield r; b; co_return r2; } ```

this transforms into

``` auto f(auto then) { a; return then(r, [&]() { b; return then(r2); }); }; ```

You can easily extend this to arbitrarily complex statements. The main thing is that obviously, you have to worry about the capture lifetime yourself (coroutines allocate a frame separate from the stack), and the syntax causes nesting for every statement (but you can avoid that using operator overloading, like C++26/29 does for executors)

Re:Looking at Unity made me understand the point of C++ coro... by spacechild1 on Wednesday March 25, 2026 @05:32PM (#47523575)
How is this better than the equivalent coroutine code? I don't see any upsides from a user's perspective.

> The main thing is that obviously, you have to worry about the capture lifetime yourself

This is a big deal! The fact that the coroutine frame is kept alive and your state can just stay in local variables is one of the main selling points. I experienced this first-hand when I rewrote callback-style C++ ASIO code to the new coroutine style. No more [self=shared_from_this()] and other shenanigans!

Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Thursday March 26, 2026 @03:01PM (#47534313)
Using shared_ptr everywhere is an antipattern.

The whole point of controlling the capture is controlling the memory layout, which is what C++ is all about.

Even with Asio, you don't really have to do this. It's just the style the examples follow, and Asio itself isn't necessarily the best design.

Re:Looking at Unity made me understand the point of C++ coro... by spacechild1 on Thursday March 26, 2026 @05:10PM (#47535787)
With callbacks you have to make sure that your data persists across the function calls. This necessarily requires more heap allocations (or copies) than in a coroutine where most data can just live on the stack.
Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Friday March 27, 2026 @05:04PM (#47548240)
A coroutine doesn't do anything more than a callback does -- it's just syntactic sugar.

The default behaviour of many asynchronous systems is to extend the lifetime of context data until all the asynchronous handlers have run. You can also just bind them to the resource instead which is arguably more elegant, but which depends on how cancellation is implemented.

Re:Looking at Unity made me understand the point of C++ coro... by spacechild1 on Saturday March 28, 2026 @11:20PM (#47560111)
Any data that needs to survive across several callback invocations requires shared ownership. This means that the data must be allocated on the heap and probably also requires reference counting. With lambdas it's also easy to make mistakes, e.g. by capturing variables on the stack by reference.

In a coroutine everything can just live on the stack because the stack frame itself is kept alive across the asynchronous function calls.

Don't you see the big difference regarding lifetime management?

Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Sunday March 29, 2026 @03:33PM (#47566416)
Not necessarily, I already explained how (resource binding).

Close the socket, all handlers are cancelled, context can be freed immediately. The socket owns the data.

Re:Looking at Unity made me understand the point of C++ coro... by spacechild1 on Sunday March 29, 2026 @04:21PM (#47566869)
> The socket owns the data.

What if there's more than one socket involved?

Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Tuesday March 31, 2026 @09:13AM (#47586888)
Then you have it owned by the parent which owns all relevant sockets.

Resource ownership management 101.

Re:Looking at Unity made me understand the point of C++ coro... by spacechild1 on Wednesday April 01, 2026 @02:10PM (#47604423)
But that's not what you want. The context object should be freed when the associated operation finishes (either successfully or with an error), not when the parent goes out of scope. With coroutines I can simply put the context object on the stack and when the task finishes, the object automatically goes out of scope.
Re:Looking at Unity made me understand the point of C++ coro... by krackers on Thursday March 26, 2026 @01:56AM (#47527047)
Isn't this basically what javascript went through with Promise chaining "callback hell" that was cleaned up with async/await (and esbuild can still desugar the latter down to the former)
Re:Looking at Unity made me understand the point of C++ coro... by mgaunard on Thursday March 26, 2026 @03:03PM (#47534335)
This is literally what coroutines are, syntactic sugar to generate nested lambdas.

Except in C++ this removes a fair amount of control given how low-level it is.