Boost logo

Boost :

Subject: Re: [boost] [coroutine] new versions
From: Giovanni Piero Deretta (gpderetta_at_[hidden])
Date: 2012-10-11 10:26:56


On Thu, Oct 11, 2012 at 2:43 PM, Oliver Kowalke <oliver.kowalke_at_[hidden]> wrote:
>
>
> > auto fn(coroutine<string(int)> c) {
> > assert(!c.has_data());
> > c(10);
> > assert(c.has_data() && c.get() == "abc");
> > return c;
> > }
> >
> > coroutine< int( string) > c( fn);
> > assert(c.has_data() && c.get() == 10);
> > c( "abc");
> > assert(c.terminated());
>
> as in one of the previous examples - your example does not work if the return value depends on the arguments passed to coroutine

well, this is true only for the first yielded return. If the
coroutine-fn must not yield a value without receiving one first, it
will have to call c.yield() to yield without value. It is ugly, I
know, it is yet another deparature from coroutines-as-functions model.
Unfortunately there is no way out of this: either the caller or the
calle must return the first message without seeing the other side's
message.

You could have two functions: make_push_coroutine() and
make_pull_coroutine() (the names are terribly long, but they are just
to give an idea) which allows the user to chose the preferred
behaviour. Another option is allowing passing additional arbitrary
parameters to the coroutine constructor, as per std::thread. The
coroutine-fn may (or not) use these parameters as if they were the
result of yielding. There shouldn't be any requirements that the
paremeters match the coroutine signature though. As with std::thread
and std::bind, and differently than operator(), the parameters are
always passed by copy and need an explicit ref() to pass by reference.
This prevent the dangling reference problem that was raised during
review.

The biggest reason for having these extra parameters is coroutine pipelining:

    {
        auto pass1 = make_coroutine([](continuation<void(int)> output) {
                for(int i = 0; i < 10; ++i)
                    output(i);
                return output;
            });
        auto pass2 = make_coroutine
                           ([](coroutine<void(double)> output,
                               coroutine<int()> input) {
                                for(auto x: input) {
                                    output(x*2);
                                }
                                return output;
                         }, std::move(pass1));
        std::vector<double> ret = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 };
        assert(std::equal(begin(pass2), end(pass2), ret.begin()));
    }

You could of course acheive the same thing by explicitly calling
std::bind, but the syntax become heavier. You can't unfortuantley rely
on c++ [=] lambda capture because a coroutine is not copyable (and [&]
doesn't really sound right).
Note that from the point of view of the pass2 coroutine-fn, there
isn't really much difference between the input and the output, they
are just two arbitrary coroutines.

-- gpd


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