cppcoro
cppcoro copied to clipboard
A generator that returns some terminal value
Sometimes I want to co_return
some additional final value from a generator. It could be:
- Some additional statistics on the data as a whole
- A reason for why the stream of values has ended
Such generator could also model a simple coroutine-style interation: When the coroutine needs the caller to provide further guidance on what it needs to do, it co_yield
s a request for that guidance. When the work is finished, the coroutine will return a value.
More specific examples:
- "ping" utility (
co_yield
s pong responses,co_return
s statistics) - Video stream (
co_yield
s frames,co_return
s reason for stopping: video ended, network troubles) - Camera controller (
co_yield
s new camera configuration,co_return
s a final, good enough shot)
So I need some generator_with_result<T, R>
that is a range and can be co_awaited
. Also asynchronous and recursive versions may be useful.
It should definitely be possible to build such a coroutine type, at least from the consumer’s point of view.
It would require a bit more thought on how to design the api for the consumer, however.
There are a few options i can think of.
-
Expose it as a range of variant<T,R> (assuming those types are different) and then always have the R value be the end of the sequence.
-
Use a different API design than begin/end/iterators. Eg have a next() method that returned a result<T,R> that indicates whether the result is an element of the sequence or the sentinel value.
auto gen = make_generator();
while (true) {
if (auto res = co_await gen.next(); res.has_value()) {
use(res.value());
} else {
use(res.sentinel());
break;
}
}
You would usually want to handle values inside the loop and the sentinel outside the loop:
auto gen = make_generator();
while (co_await gen.next()) {
use(gen.value());
}
use(gen.sentinel());
So I believe a better interface would be:
-
gen.next()
executes the coroutine, stores insidegen
avoid*
pointing to aT
or to anR
, returnstrue
if it's aT
-
gen.value()
,gen.sentinel()
access thatvoid*
Also, the generator_with_result<T, R>
name I suggested is not very good, it could be sentinel_generator<T, R>
or generator_task<T, R>
.