The review objections where to naming that operation "select" at all.
Select for 99% of C and C++ users is something which does not cancel the other waiters on return. Several mentioned how surprising it was, for them Select is something which returns when something is ready and then code might do stuff like cancel things.
I don't think there is anything wrong with the current semantics of the operation, except its name.
`select_cancelling_others()` would be fine, but I'm sure a shorter name is possible without the BSD sockets type connotations.
I checked if I could force select to only work on interruptible ops, but that's too restrictive, as it would not allow idempotent operations like timer.async_wait() that might be used as a timeout. The idea for the name comes from go. I guess a similar one is race which is used in JS.
- Andrzej took issue with the design of the generators, and I can see why. However, out in real world code land, C++ coroutine generators have surprisingly large amount of design variation. I’ve seen ones which you loop on testing them for boolean true, I’ve seen others which loop on their iterators, I’ve even seen ones which don’t implement either boolean true nor iterators and require you to call a `value()` member function which returns an Optional. Why? No idea, people seem to innovate uselessly here for some reason. I think it wise to adhere to `std::generator` from the standard, and do exactly what it does. From my reading of Async’s `generator`, whilst it has extensions e.g. the invocable call operator, the only thing missing is the iterator interface to make it implement `std::generator` and I don’t see any obvious reason why an iterator interface can’t be added here?
The reason is that we don't have "async for". I would need to resume the generator on increment, but if `itr++` is an awaitable expression that needs to be co_awaited, it's not an iterator. If there was an async for (or a common pattern) I'd make generator work with it.
Y'see I don't see the need for async for. That's separate to this.
`std::generator` provides iterators. When you iterate one of its iterators the coroutine gets resumed and it yields a new value.
The reason `std::generator` is like this is because it can be called from non-coroutine code, so `co_await` isn't available. Implied in this is iterator implementations must pump the coroutine by hand.
This is very straightforward for the simple case, it gets harder if the generator suspends on say i/o. Then your generator iterator implementation needs to pump the event loop too. This isn't a hard to surmount problem to solve for Boost.Async I think. And I think a Boost level library does need to implement what the C++ standard demands, even if it's a bit dumb.
Ah, gotcha I thought you wanted that to be in async, i.e. with a co_await somewhere. Yeah that could be done & might actually be something useful for non-pushable generators. I'll look into it.
Down the road if WG21 ever gets round to async for, support for that can be added later. I have also wondered if a `BOOST_CO_ASYNC_FOR` macro couldn't implement async for right now.
And finally, I have a gut feeling without proof nor evidence that it *may* be possible to detect from a macro whether it is being invoked by a coroutine or not, and if so, "do the right thing" based on caller context. So a `BOOST_ASYNC_FOR()` macro which does the right kind of for loop for iterating a generator depending on caller may be achievable.
I couldn't think of a way to detect that. I have a very simple BOOST_ASYNC_FOR macro here Documentation https://klemens.dev/async/reference.html#async_for It's more of a while loop though, so I might need to reconsider that one as well.