File: asymmetric.qbk

package info (click to toggle)
boost1.88 1.88.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 576,932 kB
  • sloc: cpp: 4,149,234; xml: 136,789; ansic: 35,092; python: 33,910; asm: 5,698; sh: 4,604; ada: 1,681; makefile: 1,633; pascal: 1,139; perl: 1,124; sql: 640; yacc: 478; ruby: 271; java: 77; lisp: 24; csh: 6
file content (637 lines) | stat: -rw-r--r-- 22,871 bytes parent folder | download | duplicates (3)
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
[/
          Copyright Oliver Kowalke 2014.
 Distributed under the Boost Software License, Version 1.0.
    (See accompanying file LICENSE_1_0.txt or copy at
          http://www.boost.org/LICENSE_1_0.txt
]

[section:asymmetric Asymmetric coroutine]

Two asymmetric coroutine types - __push_coro__ and __pull_coro__ - provide a
unidirectional transfer of data.
[note ['asymmetric_coroutine<>] is a typedef of __coro__.]


[heading __pull_coro__]
__pull_coro__ transfers data from another execution context (== pulled-from).
The template parameter defines the transferred parameter type.
The constructor of __pull_coro__ takes a function (__coro_fn__) accepting a
reference to an __push_coro__ as argument. Instantiating an __pull_coro__ passes
the control of execution to __coro_fn__ and a complementary __push_coro__ is
synthesized by the library and passed as reference to __coro_fn__.

This kind of coroutine provides __pull_coro_op__. This method only switches
context; it transfers no data.

__pull_coro__ provides input iterators (__pull_coro_it__) and __begin__/__end__
are overloaded. The increment-operation switches the context and transfers data.

        typedef boost::coroutines2::coroutine<int>   coro_t;

        coro_t::pull_type source(
            [&](coro_t::push_type& sink){
                int first=1,second=1;
                sink(first);
                sink(second);
                for(int i=0;i<8;++i){
                    int third=first+second;
                    first=second;
                    second=third;
                    sink(third);
                }
            });

        for(auto i:source)
            std::cout << i <<  " ";

        output:
        1 1 2 3 5 8 13 21 34 55

In this example an __pull_coro__ is created in the main execution context taking
a lambda function (== __coro_fn__) which calculates Fibonacci numbers in a
simple ['for]-loop.
The __coro_fn__ is executed in a newly created execution context which is
managed by the instance of __pull_coro__.
An __push_coro__ is automatically generated by the library and passed as
reference to the lambda function. Each time the lambda function calls
__push_coro_op__ with another Fibonacci number, __push_coro__ transfers it back
to the main execution context. The local state of __coro_fn__ is preserved and
will be restored upon transferring execution control back to __coro_fn__
to calculate the next Fibonacci number.
Because __pull_coro__ provides input iterators and __begin__/__end__ are
overloaded, a ['range-based for]-loop can be used to iterate over the generated
Fibonacci numbers.


[heading __push_coro__]
__push_coro__ transfers data to the other execution context (== pushed-to).
The template parameter defines the transferred parameter type.
The constructor of __push_coro__ takes a function (__coro_fn__) accepting a
reference to an __pull_coro__ as argument. In contrast to __pull_coro__,
instantiating an __push_coro__ does not pass the control of execution to
__coro_fn__ - instead the first call of __push_coro_op__ synthesizes a
complementary __pull_coro__ and passes it as reference to __coro_fn__.

The __push_coro__ interface does not contain a ['get()]-function: you can not retrieve
values from another execution context with this kind of coroutine.

__push_coro__ provides output iterators (__push_coro_it__) and
__begin__/__end__ are overloaded. The increment-operation switches the context
and transfers data.

        typedef boost::coroutines2::coroutine<std::string>   coro_t;

        struct FinalEOL{
            ~FinalEOL(){
                std::cout << std::endl;
            }
        };

        const int num=5, width=15;
        coro_t::push_type writer(
            [&](coro_t::pull_type& in){
                // finish the last line when we leave by whatever means
                FinalEOL eol;
                // pull values from upstream, lay them out 'num' to a line
                for (;;){
                    for(int i=0;i<num;++i){
                        // when we exhaust the input, stop
                        if(!in) return;
                        std::cout << std::setw(width) << in.get();
                        // now that we've handled this item, advance to next
                        in();
                    }
                    // after 'num' items, line break
                    std::cout << std::endl;
                }
            });

        std::vector<std::string> words{
            "peas", "porridge", "hot", "peas",
            "porridge", "cold", "peas", "porridge",
            "in", "the", "pot", "nine",
            "days", "old" };

        std::copy(begin(words),end(words),begin(writer));

        output:
                   peas       porridge            hot           peas       porridge
                   cold           peas       porridge             in            the
                    pot           nine           days            old

In this example an __push_coro__ is created in the main execution context
accepting a lambda function (== __coro_fn__) which requests strings and lays out
'num' of them on each line.
This demonstrates the inversion of control permitted by coroutines. Without
coroutines, a utility function to perform the same job would necessarily
accept each new value as a function parameter, returning after processing that
single value. That function would depend on a static state variable. A
__coro_fn__, however, can request each new value as if by calling a function
-- even though its caller also passes values as if by calling a function.
The __coro_fn__ is executed in a newly created execution context which is
managed by the instance of __push_coro__.
The main execution context passes the strings to the __coro_fn__ by calling
__push_coro_op__.
An __pull_coro__ instance is automatically generated by the library and passed as
reference to the lambda function. The __coro_fn__ accesses the strings passed
from the main execution context by calling __pull_coro_get__ and lays those
strings out on ['std::cout] according the parameters 'num' and 'width'.
The local state of __coro_fn__ is preserved and will be restored after
transferring execution control back to __coro_fn__.
Because __push_coro__ provides output iterators and __begin__/__end__ are
overloaded, the ['std::copy] algorithm can be used to iterate over the vector
containing the strings and pass them one by one to the coroutine.


[heading coroutine-function]
The __coro_fn__ returns ['void] and takes its counterpart-coroutine as
argument, so that using the coroutine passed as argument to __coro_fn__ is the
only way to transfer data and execution control back to the caller.
Both coroutine types take the same template argument.
For __pull_coro__ the __coro_fn__ is entered at __pull_coro__ construction.
For __push_coro__ the __coro_fn__ is not entered at __push_coro__ construction
but entered by the first invocation of __push_coro_op__.
After execution control is returned from __coro_fn__ the state of the
coroutine can be checked via __pull_coro_bool__ returning `true` if the
coroutine is still valid (__coro_fn__ has not terminated). Unless the first
template parameter is `void`, `true` also implies that a data value is
available.


[heading passing data from a pull-coroutine to main-context]
In order to transfer data from an __pull_coro__ to the main-context the framework
synthesizes an __push_coro__ associated with the __pull_coro__ instance in the
main-context. The synthesized __push_coro__ is passed as argument to __coro_fn__.
The __coro_fn__ must call this __push_coro_op__ in order to transfer each
data value back to the main-context.
In the main-context, the __pull_coro_bool__ determines whether the coroutine is
still valid and a data value is available or __coro_fn__ has terminated
(__pull_coro__ is invalid; no data value available). Access to the transferred
data value is given by __pull_coro_get__.

        typedef boost::coroutines2::coroutine<int>   coro_t;

        coro_t::pull_type source( // constructor enters coroutine-function
            [&](coro_t::push_type& sink){
                sink(1); // push {1} back to main-context
                sink(1); // push {1} back to main-context
                sink(2); // push {2} back to main-context
                sink(3); // push {3} back to main-context
                sink(5); // push {5} back to main-context
                sink(8); // push {8} back to main-context
            });

        while(source){            // test if pull-coroutine is valid
            int ret=source.get(); // access data value
            source();             // context-switch to coroutine-function
        }


[heading passing data from main-context to a push-coroutine]
In order to transfer data to an __push_coro__ from the main-context the framework
synthesizes an __pull_coro__ associated with the __push_coro__ instance in the
main-context. The synthesized __pull_coro__ is passed as argument to __coro_fn__.
The main-context must call this __push_coro_op__ in order to transfer each data
value into the __coro_fn__.
Access to the transferred data value is given by __pull_coro_get__.

        typedef boost::coroutines2::coroutine<int>   coro_t;

        coro_t::push_type sink( // constructor does NOT enter coroutine-function
            [&](coro_t::pull_type& source){
                for (int i:source) {
                    std::cout << i <<  " ";
                }
            });

        std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
        for( int i:v){
            sink(i); // push {i} to coroutine-function
        }


[heading accessing parameters]
Parameters returned from or transferred to the __coro_fn__ can be accessed with
__pull_coro_get__.

Splitting-up the access of parameters from context switch function enables to
check if __pull_coro__ is valid after return from __pull_coro_op__, e.g.
__pull_coro__ has values and __coro_fn__ has not terminated.

        typedef boost::coroutines2::coroutine<boost::tuple<int,int>> coro_t;

        coro_t::push_type sink(
            [&](coro_t::pull_type& source){
                // access tuple {7,11}; x==7 y==1
                int x,y;
                boost::tie(x,y)=source.get();
            });

        sink(boost::make_tuple(7,11));


[heading exceptions]
An exception thrown inside an __pull_coro__'s __coro_fn__ before its first call
to __push_coro_op__ will be re-thrown by the __pull_coro__ constructor. After an
__pull_coro__'s __coro_fn__'s first call to __push_coro_op__, any subsequent
exception inside that __coro_fn__ will be re-thrown by __pull_coro_op__.
__pull_coro_get__ does not throw.

An exception thrown inside an __push_coro__'s __coro_fn__ will be re-thrown by
__push_coro_op__.

[important Code executed by __coro_fn__ must not prevent the propagation of the
__forced_unwind__ exception.  Absorbing that exception will cause stack
unwinding to fail.  Thus, any code that catches all exceptions must re-throw any
pending __forced_unwind__ exception.]

        try {
            // code that might throw
        } catch(const boost::context::detail::forced_unwind&) {
            throw;
        } catch(...) {
            // possibly not re-throw pending exception
        }

[important Do not jump from inside a catch block and than re-throw the
exception in another execution context.]


[heading Stack unwinding]
Sometimes it is necessary to unwind the stack of an unfinished coroutine to
destroy local stack variables so they can release allocated resources (RAII
pattern). The `attributes` argument of the coroutine constructor
indicates whether the destructor should unwind the stack (stack is unwound by
default).

Stack unwinding assumes the following preconditions:

* The coroutine is not __not_a_coro__
* The coroutine is not complete
* The coroutine is not running
* The coroutine owns a stack

After unwinding, a __coro__ is complete.

        struct X {
            X(){
                std::cout << "X()" << std::endl;
            }

            ~X(){
                std::cout << "~X()" << std::endl;
            }
        };

        {
            typedef boost::coroutines2::coroutine<void> coro_t;

            coro_t::push_type sink(
                [&](coro_t::pull_type& source){
                    X x;
                    for(int i = 0; ; ++i){
                        std::cout << "fn(): " << i << std::endl;
                        // transfer execution control back to main()
                        source();
                    }
                });

            sink();
            sink();
            sink();
            sink();
            sink();

            std::cout<<"sink is complete: "<<std::boolalpha<<!sink<<"\n";
        }

        output:
            X()
            fn(): 0
            fn(): 1
            fn(): 2
            fn(): 3
            fn(): 4
            sink is complete: false
            ~X()


[heading Range iterators]
__boost_coroutine__ provides output- and input-iterators using __boost_range__.
__pull_coro__ can be used via input-iterators using __begin__ and __end__.

        typedef boost::coroutines2::coroutine< int > coro_t;

        int number=2,exponent=8;
        coro_t::pull_type source(
            [&](coro_t::push_type & sink){
                int counter=0,result=1;
                while(counter++<exponent){
                    result=result*number;
                    sink(result);
                }
            });

        for (auto i:source)
            std::cout << i <<  " ";

        output:
            2 4 8 16 32 64 128 256

['coroutine<>::pull_type::iterator::operator++()] corresponds to
__pull_coro_op__; ['coroutine<>::pull_type::iterator::operator*()]
roughly corresponds to __pull_coro_get__. An iterator originally obtained from
__begin__ of an __pull_coro__ compares equal to an iterator obtained from
__end__ of that same __pull_coro__ instance when its __pull_coro_bool__ would
return `false`].

[note If `T` is a move-only type, then
['coroutine<T>::pull_type::iterator] may only be dereferenced once
before it is incremented again.]

Output-iterators can be created from __push_coro__.

        typedef boost::coroutines2::coroutine<int>   coro_t;

        coro_t::push_type sink(
            [&](coro_t::pull_type& source){
                while(source){
                    std::cout << source.get() <<  " ";
                    source();
                }
            });

        std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
        std::copy(begin(v),end(v),begin(sink));

['coroutine<>::push_type::iterator::operator*()] roughly
corresponds to __push_coro_op__. An iterator originally obtained from
__begin__ of an __push_coro__ compares equal to an iterator obtained from
__end__ of that same __push_coro__ instance when its __push_coro_bool__ would
return `false`.


[heading Exit a __coro_fn__]
__coro_fn__ is exited with a simple return statement jumping back to the calling
routine. The __pull_coro__, __push_coro__ becomes complete, e.g. __pull_coro_bool__,
__push_coro_bool__ will return `false`.

[important After returning from __coro_fn__ the __coro__ is complete (can not
resumed with __push_coro_op__, __pull_coro_op__).]



[section:pull_coro Class `coroutine<>::pull_type`]

    #include <boost/coroutine2/coroutine.hpp>

    template< typename R >
    class coroutine<>::pull_type
    {
    public:
        template< typename Fn >
        pull_type( Fn && fn);

        template< typename StackAllocator, typename Fn >
        pull_type( StackAllocator stack_alloc, Fn && fn);

        pull_type( pull_type const& other)=delete;

        pull_type & operator=( pull_type const& other)=delete;

        ~pull_type();

        pull_type( pull_type && other) noexcept;

        pull_type & operator=( pull_type && other) noexcept;

        pull_coroutine & operator()();

        explicit operator bool() const noexcept;

        bool operator!() const noexcept;

        R get() noexcept;
    };

    template< typename R >
    range_iterator< pull_type< R > >::type begin( pull_type< R > &);

    template< typename R >
    range_iterator< pull_type< R > >::type end( pull_type< R > &);

[heading `template< typename Fn >
          pull_type( Fn && fn)`]
[variablelist
[[Effects:] [Creates a coroutine which will execute `fn`, and enters it.]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]

[heading `template< typename StackAllocator, typename Fn >
          pull_type( StackAllocator const& stack_alloc, Fn && fn)`]
[variablelist
[[Effects:] [Creates a coroutine which will execute `fn`.
For allocating/deallocating the stack `stack_alloc` is used.]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]

[heading `~pull_type()`]
[variablelist
[[Effects:] [Destroys the context and deallocates the stack.]]
]

[heading `pull_type( pull_type && other)`]
[variablelist
[[Effects:] [Moves the internal data of `other` to `*this`.
`other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]

[heading `pull_type & operator=( pull_type && other)`]
[variablelist
[[Effects:] [Destroys the internal data of `*this` and moves the
internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]

[heading `explicit operator bool() const noexcept`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `false`. Otherwise `true`.]]
[[Throws:] [Nothing.]]
]

[heading `bool operator!() const noexcept`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `true`. Otherwise `false`.]]
[[Throws:] [Nothing.]]
]

[heading `pull_type<> & operator()()`]
[variablelist
[[Preconditions:] [`*this` is not a __not_a_coro__.]]
[[Effects:] [Execution control is transferred to __coro_fn__ (no parameter is
passed to the coroutine-function).]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]

[heading `R get() noexcept`]

    R    coroutine<R,StackAllocator>::pull_type::get();
    R&   coroutine<R&,StackAllocator>::pull_type::get();
    void coroutine<void,StackAllocator>::pull_type::get()=delete;

[variablelist
[[Preconditions:] [`*this` is not a __not_a_coro__.]]
[[Returns:] [Returns data transferred from coroutine-function via
__push_coro_op__.]]
[[Throws:] [`invalid_result`]]
[[Note:] [If `R` is a move-only type, you may only call `get()` once before
the next __pull_coro_op__ call.]]
]

[heading Non-member function `begin( pull_type< R > &)`]
    template< typename R >
    range_iterator< pull_type< R > >::type begin( pull_type< R > &);

[variablelist
[[Returns:] [Returns a range-iterator (input-iterator).]]
]

[heading Non-member function `end( pull_type< R > &)`]
    template< typename R >
    range_iterator< pull_type< R > >::type end( pull_type< R > &);

[variablelist
[[Returns:] [Returns an end range-iterator (input-iterator).]]
[[Note:] [When first obtained from `begin( pull_type< R > &)`, or after some
number of increment operations, an iterator will compare equal to the iterator
returned by `end( pull_type< R > &)` when the corresponding __pull_coro_bool__
would return `false`.]]
]

[endsect]


[section:push_coro Class `coroutine<>::push_type`]

    #include <boost/coroutine2/coroutine.hpp>

    template< typename Arg >
    class coroutine<>::push_type
    {
    public:
        template< typename Fn >
        push_type( Fn && fn);

        template< typename StackAllocator, typename Fn >
        push_type( StackAllocator stack_alloc, Fn && fn);

        push_type( push_type const& other)=delete;

        push_type & operator=( push_type const& other)=delete;

        ~push_type();

        push_type( push_type && other) noexcept;

        push_type & operator=( push_type && other) noexcept;

        explicit operator bool() const noexcept;

        bool operator!() const noexcept;

        push_type & operator()( Arg arg);
    };

    template< typename Arg >
    range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);

    template< typename Arg >
    range_iterator< push_type< Arg > >::type end( push_type< Arg > &);

[heading `template< typename Fn >
          push_type( Fn && fn)`]
[variablelist
[[Effects:] [Creates a coroutine which will execute `fn`.]]
]

[heading `template< typename StackAllocator, typename Fn >
          push_type( StackAllocator const& stack_alloc, Fn && fn)`]
[variablelist
[[Effects:] [Creates a coroutine which will execute `fn`.
For allocating/deallocating the stack `stack_alloc` is used.]]
]

[heading `~push_type()`]
[variablelist
[[Effects:] [Destroys the context and deallocates the stack.]]
]

[heading `push_type( push_type && other) noexcept`]
[variablelist
[[Effects:] [Moves the internal data of `other` to `*this`.
`other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]

[heading `push_type & operator=( push_type && other) noexcept`]
[variablelist
[[Effects:] [Destroys the internal data of `*this` and moves the
internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]

[heading `explicit operator bool() const noexcept`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `false`. Otherwise `true`.]]
[[Throws:] [Nothing.]]
]

[heading `bool operator!() const noexcept`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `true`. Otherwise `false`.]]
[[Throws:] [Nothing.]]
]

[heading `push_type & operator()(Arg arg)`]

        push_type& coroutine<Arg>::push_type::operator()(Arg);
        push_type& coroutine<Arg&>::push_type::operator()(Arg&);
        push_type& coroutine<void>::push_type::operator()();

[variablelist
[[Preconditions:] [operator unspecified-bool-type() returns `true` for `*this`.]]
[[Effects:] [Execution control is transferred to __coro_fn__ and the argument
`arg` is passed to the coroutine-function.]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]

[heading Non-member function `begin( push_type< Arg > &)`]
    template< typename Arg >
    range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);

[variablelist
[[Returns:] [Returns a range-iterator (output-iterator).]]
]

[heading Non-member function `end( push_type< Arg > &)`]
    template< typename Arg >
    range_iterator< push_type< Arg > >::type end( push_type< Arg > &);

[variablelist
[[Returns:] [Returns a end range-iterator (output-iterator).]]
[[Note:] [When first obtained from `begin( push_type< R > &)`, or after some
number of increment operations, an iterator will compare equal to the iterator
returned by `end( push_type< R > &)` when the corresponding __push_coro_bool__
would return `false`.]]
]

[endsect]



[endsect]