Boost logo

Boost :

From: Xi Wang (xi.wang_at_[hidden])
Date: 2006-05-06 11:45:00


On 5/6/06, Giovanni P. Deretta <gpderetta_at_[hidden]> wrote:
>
> No, yield returns an actual tuple. The element types of the tuple are
> the same types of the function arguments. For example:
>
> tuple<a_t, b_t, c_t> some_function
> (corutine<tuple<a_t, b_t, c_t>(d_t, e_t, f_t)>& self,
> d_t d,
> e_t e,
> f_t f) { // 1
> a_t a;
> b_t b;
> c_t c;
> while(true) {
> do_something_with(d, e, f);
> a = some_value;
> b = some_other_value;
> d = yet_some_other_value;
> tie(d, e, f) = self.yield (a, b, d); // 2
> }
> }
>
> int main() {
>
> coroutine<corutine<tuple<a_t, b_t, c_t>(d_t, e_t, f_t)>
> my_coroutine(some_function);
>
> a_t a;
> b_t b;
> c_t c;
> d_t d = ...;
> e_t e = ...;
> f_t f = ...;
>
> tie(a, b, c) = my_coroutine(d, e, f); // 3
> do_something with a, b, c, d, e, f;
> tie(a, b, c) = my_coroutine(d, e, f); // 4
>
> }
>
> The first time a coroutine is entered (from 3), execution start at the
> main entry point (1). Each tuple can have an arbitrary number of
> arguments and results (using tuples), but their type and number is fixed
> at compile time. At the yield point control is relinquished to the
> caller. The argument values of yield will be returned to the caller as
> if the coroutine function had returned. (Note that yield is special
> cased for the tuple case and one is not required to write
> self.yield(make_tuple(...))). Yield is *not* the same thing as return
> because the current scope is not exited, stack is not unwound and thus
> scoped objects are not destroyed. This allows us to reenter the scope.
>
> Next time the coroutine is invoked (note that for the caller there is
> *no* syntax distinction from first time invocation and successive ones),
> control is resumed at the yield point. Yield gives to the coroutine the
> values passed as parameter by the caller. As the parameter types and
> numbers are the same of the main entry point (i.e. the function
> signature), yield must know it (or be checked at run time).

Got it. Thanks a lot for the detailed explanation :-)
So the return value (tuple) of a call to a yield in the coroutine body is
the parameters of the subsequent invocation to the coroutine object, right?
I was thinking it would return nothing.

I'm a strong believer of static type checking and I think that
> coroutines should be statically checked. Of course I see the usefulness
> of postponing checks until runtime. a coroutine<boost::any(boost::any)>
> would do it, and the library could have special support for it. But the
> preferred interface would be statically checked.
>
> > If the point is to check the return type, maybe we can trick the
> compiler
> > like this:
> > int __yield(T);
> > #define yield(val) if (__yield(val) < 0) return val;
> > If the return value of __yield should not be less than 0, the compiler
> would
> > check
> > the type of "val" automatically.
> >
>
> Apart for my general dislike for macros (except when you can't really do
> without :) ), this wouldn't work because return would exit the scope and
> cause all locals to be destroyed.
>
> It is possible to implement a kind of pseudo-coroutines using a
> variation of the duff device (see
> http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html), where the
> yield is actually substituted by return, but they are severely limited.
> All your locals become static, thus are thread unsafe; it uses unsafe
> macros; the syntax is not natural as it requires special macros; and
> most importantly you can't have multiple instances of the same coroutine.

 I dislike macros, too:-)

The "return" I mentioned was just to guide the compiler to check the return
type
rather than to exit the function (of course it should not). More clearly,
suppose real_yield is a global yield function, not type-safe.
#define yield(x) \
    if (FALSE) return x; \
    real_yield(x);
This macro would help to check the return type, and "return x" is never
called.
However, in this case, the return value (tuple) of real_yield could not be
retrieved.
So just forget it:-)

> coroutine::operator() always invokes a coroutine. Binding is done only
> at creation time (in the constructor). I thought the article was clear.
> If you could point out where the confusion is, I will try to make it
> more clear.
>
> Also consider that in pseudocode 'coroutine' is not an object but a
> keyword that introduces (possibly lambda) a coroutine body. In the
> pseudocode every 'coroutine' body is a diferent instance. Should I make
> this more clear?.

I got it. Thanks.


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