File: defining.yo

package info (click to toggle)
c%2B%2B-annotations 12.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 13,044 kB
  • sloc: cpp: 24,337; makefile: 1,517; ansic: 165; sh: 121; perl: 90
file content (111 lines) | stat: -rw-r--r-- 6,352 bytes parent folder | download
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
NOUSERMACRO(operator)

When defining coroutines the tthi(coroutine) header file must be included.

A function is a coroutine once it uses the keywords ti(co_yield),
ti(co_await), or ti(co_return). A coroutines cannot use the tt(return)
keyword, cannot define variadic parameters, and its return type must be an
existing type, which defines the hi(handler (coroutine)) em(coroutine's
handler).

Although coroutines appear to return objects (as suggested by the tt(Fibo)
return type of the tt(Fibo fiboCoro()) coroutine defined in the
previous section), in fact they do not. Instead coroutines return
so-called `handlers'. Such a handler is tt(fibo), defined and used in the
previous section's tt(main) function:
        verbinsert(-as4 demo/fibocoro/main.2)

The class tt(Fibo) itself defines the characteristics allowing the compiler to
generate code storing the coroutine's arguments, its local variables, the
location of the next instruction to execute when the coroutine returns or is
suspended, and its so-called emi(promise_type) object on the heap. This only
happens once, so when the coroutine is activated (as in tt(sum +=
fibo.next())) the steps which are normally taken when a function is called are
avoided, and instead the coroutine is immediately executed, using its already
available local variables and arguments.
    hi(future (coroutine))hi(promise_type (coroutine))hi(cooperating routine) 
    Coroutine's handler classes are sometimes called tt(Future), and their
nested state classes em(must) be known as the handler's tt(promise_type). The
names em(future) and em(promise_type) are completely unrelated to the
tt(std::future) (cf. section ref(FUTURE)) and tt(std::promise) (cd. section
ref(PROMISE)) types which are used in the context of multi threading. In fact,
coroutines themselves are unrelated to multi threading, but are known as
em(cooperating routines). Because the coroutines' handler and state classes
are unrelated to the tt(future) and tt(promise) classes used in the context of
multi threading in this chapter the terms em(Handler) and tt(State) are
generally used.

It's one thing to define a coroutine, but when using a coroutine its
handler-class (the tt(Fibo) class in the current example) must also be
defined. In addition, hi(promise_type) such a handler-class em(must) define a
nested class whose name em(must) be publicly available as the handler's
tt(promise_type). The name tt(promise_type) doesn't very well cover its
purpose, and using a more descriptive class name might be preferred. In that
case a simple using declaration in the handler class's public section can be
used, as shown in the following basic design of the tt(Fibo) handler-class:
        verbinsert(-as4 demo/fibocoro/fibo/fibo.2)
    
The coroutine's handler class has the following characteristics:
    itemization(
    it() it has a nested class (here: tt(State)) keeping track of the
        couroutine's state;
    it() it commonly defines a private data member of the type
        ti(std::coroutine_handle<State>), e.g.,
        tt(std::coroutine_handle<State> d_handle), whose members are covered
        below;
    it() Unless the handling class's nested class is called tt(promise_type) a
        using declaraction must be specified to make the nested class name
        also known as tt(promise_type);
    it() Other members are optional, although usually there is at least a
        member returning a value that's available in the coroutine's state,
        like the member tt(next) which was used in the example's tt(main)
        function: 
       verb(
    sum += fibo.next();
       )
       The tt(next) member's current implementation resumes the coroutine.
        This doesn't mean that when tt(next) is called for the first time,
        that it's the very first activation of the coroutine: the call tt(auto
        fibo = fiboCoro()) comes first, and constructing and returning the
        coroutine's handler (thereby suspending the coroutine at its very
        first statement) is done automatically. At that point the caller
        receives the coroutine's handler object, and constructing and
        returning the handler object isn't visible in the coroutine's code
        (more about that in the next section). Once suspended at tt(co_yield)
        the value that's available in the coroutine's tt(State) is returned
        and made available to the coroutine's caller:
       verbinsert(-s4 //next demo/fibocoro/fibo/next.cc)
    )

    The following members can be called via the Handler's tt(d_handle) data
        member:
    itemization(
    itt(void *address()) hi(address) returning the address of the handler's
        tt(State) object;
    itt(void destroy()), hi(destroy) returning the tt(State) object's memory
        to the operating system. It ends the tt(State) object's
        existence. Usually the handler class's destructor calls
        tt(d_handle.destroy()).
    itt(bool done(),) hi(done) returning tt(true) when the coroutine has
        returned, and tt(false) if it's currently suspended;
    itt(coroutine_handle from_address(void *address)) hi(from_address) returns
        a tt(coroutine_handle) corresponding to the address of a handler's
        tt(State) object, which address is passed to the function as its
        argument. A tt(nullptr) can also be passed to tt(from_address);
    itt(explicit operator bool(),) hi(operator bool [coroutine]) returning
        tt(true) if tt(d_handle) is not a null-pointe. It's commonly used in
        the handler's destructor's tt(if (d_handle)) phrase. It returns
        tt(false) after assigning 0 (or tt(nullptr)) to tt(d_handle). This
        operator and tt(static_cast<bool>(d_handle.address())) act identically
        (note that tt(d_handle.address()) is still valid after assigning 0 to
        tt(d_handle), in which case it returns 0);
    itt(State &promise(),) hi(promise [coroutine]) returning a reference to
        the tt(Handler's State) class.
    itt(void resume()) hi(resume) hi(operator() [coroutine])
        (or tt(void operator()())) resumes the execution of a suspended
        coroutine. Resuming a coroutine is only defined if the coroutine is
        actually suspended.
    )

The tt(Handler's State) class keeps track of the coroutine's state. Its basic
elements are covered in the next section.