Boost logo

Boost :

Subject: Re: [boost] {Review] Coroutine reviewstarts today, September 3rd
From: Eugene Yakubovich (eyakubovich_at_[hidden])
Date: 2012-09-10 18:07:27


Hi Oliver,

> I managed to implement your suggestion - generator-functions are required to
> return void and generator<>::self_t::yield() accepts the return type
> specified as template arg of generator<>.
> generator<>::self_t::yield_break() was removed.
>
> The code is available under git://gitorious.org/boost-dev/boost-dev.git
> (branch coroutine).
>

Thank you for implementing the changes. I know I initially argued to
eliminate yield_break but later agreed that it was probably beneficial
to have it for the cases when one wants to complete the generator from
non top-level function. With the exception machinery there anyway, I
think it won't hurt to keep it.

Another issue that I see is with reference/pointer types. The fact
that the following code will blow up is troubling:

struct X {...};
typedef boost::coro::generator<X*> gen_t;

void foo(gen_t::self_t& self) {
    X x1, x2;
    self.yield(&x1);
    self.yield(&x2);
}

gen_t g(foo);
while( g ) {
    X* val = g();
    // use *val. Ok on first iteration, dangling pointer on the second!!!
}

Especially if generator does not support movable-only types, I am
afraid that this will be a common tripping point. Giovanni's design
had the same issue which he advised to avoid by using the iterator
like interface (using operator* and operator++).

Maybe we can change behavior so that generator's constructor does not
invoke the generator-function, only operator() does. And change
operator() to return optional<R> instead of R. The usage can then be:

gen_t g(foo);
while( optional<X> val = g() ) {
    // use val.get()
}

I'm not really sure about what to do about is_complete() or operator
unspecified-bool-type(), though. Maybe they are not needed at all? I
admit that this interface might be a bit quirky but if the library
also provides input iterator, that can become the primary interface.
Generators are used to produce sequences and iterators are the central
mechanism of dealing with sequences in C++. So the iterator usage
could be:

for( gen_t::iterator it = g.begin(); it != g.end(); ++it ) {
    X* val = *it; // no dangling pointers
}

(or BOOST_FOREACH, C++11 range-based for, or for_each).

g.begin() or gen_t::iterator ctor would invoke the generator-function
for the first time and ++it for all subsequent times.

An alternative approach could be to try to make generator itself into
an input iterator (like Giovanni's design) but that is hard because
generator is non-copyable.

Thoughts?

Regards,
-Eugene


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk