1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
As we've seen, when a coroutine starts it constructs and returns an object of
its handler class. The handler class contains a subclass whose object keeps
track of the coroutine's state. In this chapter that subclass is named
tt(State), and a using declaration is used to make it known as
tt(promise_type) which is required by the standard facilities made available
for coroutines.
When coroutines are suspended at tt(co_yield) statements, the yielded values
are passed to tt(State) class's tt(yield_value) members whose parameter
types match the types of the yielded values.
In this section we reverse our point of view, and discuss a method allowing
the coroutine to reach facilities of the tt(State) class. We've already
encountered one way to pass information from the coroutine to the tt(State)
class: if the tt(State) class's constructor defines the same parameters as the
coroutine itself then that constructor is used, receiving the coroutine's
parameters as arguments.
But let's assume that the coroutine performs a continuous loop containing
several, maybe conditional, tt(co_yield) statements, and we want to inform the
tt(State) class what the current iteration cycle is. In that case a parameter
is less suitable, as tracking the cycle number is in fact a job for one of the
local variables of the coroutine, which would look something like this:
verb( Handler coroutine()
{
size_t cycleNr = 0;
// make cycleNr available to tt(Handler's State) class
while (true)
{
++cycleNr; // now also known to tt(Handler's State)
... // the coroutine at work, using various co_yield
// statements
}
})
Awaiters can also be used in these kinds of situations, setting up
communication lines between coroutines and the tt(State) classes of their
tt(Handler) class objects. As an illustration, the original
lref(fibocoro coroutine)(FIBOCORO) was slightly modified:
verbinsert(-ns4 //fibo demo/fibocoroawaiter/fibocoroutine.cc)
itemization(
it() At line 5 tt(size_t cycle) is defined, keeping track of the
coroutine's iteration cycle;
it() Line 7 contains a tt(co_await) statemenent, passing tt(co_await) an
object that receives the tt(cycle), which is the variable the
tt(Handler's State) should know about;
it() At line 12 tt(cycle) is incremented, so it contains the current
iteration cycle.
)
The tt(Awaiter) object, since there's no tt(State::await_transform) member,
is an awaitable. Neither does tt(Awaiter) have a tt(Type operator
co_await()), so the anonymous tt(Awaiter) object is indeed an Awaiter.
Being the Awaiter, it defines three members: tt(await_ready), merely returning
tt(false), as the coroutine's execution must be suspended at the tt(co_await)
statement; tt(await_suspend(handle)), receiving a handle to the coroutine's
tt(Handler's State) object; and tt(await_resume), which doesn't have to do
anything at all:
verbinsert(-s4 //awaiter demo/fibocoroawaiter/awaiter/awaiter.h)
The member tt(await_suspend) uses the received handle to access the tt(State)
object, passing tt(cycle) to tt(State::setCycle):
verbinsert(-s4 //+awaiter demo/fibocoroawaiter/awaiter/awaitsuspend.cc)
In the next section (ref(FSACORO)) we use tt(await_suspend) to switch from
one coroutine to another, but that's not required here. So the member returns
tt(false), and thus continues its execution once it has passed tt(cycle) to
tt(State::setCycle). This way coroutines can pass information to the
tt(Handler's State) object, which could define a data member tt(size_t const
*d_cycle) and a member tt(setCycle), using tt(d_cycle) in, e.g.,
tt(yield_value):
verbinsert(-s4 //cycle demo/fibocoroawaiter/fibo/fibo.h)
verbinsert(-s4 //cycle demo/fibocoroawaiter/fibo/suspendalways.cc)
|