-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoroutine.qbk
806 lines (615 loc) · 24.8 KB
/
coroutine.qbk
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
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
[/
Copyright Oliver Kowalke 2009.
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:coroutine Coroutine]
Each instance of __coro__ has its own context of execution (CPU registers and
stack space) or represents __not_a_coro__ (similar to __thread__).
Objects of type __coro__ are moveable but not copyable and can be returned by a
function.
boost::coroutines::coroutine< void() > f();
void g()
{
boost::coroutines::coroutine< void() > c( f() );
c();
}
[note __boost_move__ is used to emulate rvalue references.]
[heading Creating a coroutine]
A new __coro__ is created from a __coro_fn__ (function or functor) which will be
executed in a new __ctx__ (CPU registers and stack space).
[note __coro_fn__ is required to return ['void] and accept a reference of type
__coro_caller__.]
The template argument __signature__ determines the data-types transferred to
__coro_fn__ and from __coro_fn__ by calling __coro_op__ and __coro_get__.
typedef boost::coroutines::coroutine< int( std::string const&) > coro_t;
// void f( boost::coroutine< std::string const&( int) > & ca)
void f( coro_t::caller_type & ca)
{
...
// access argument
std::string str( ca.get() );
...
ca( 7);
...
}
std::string str;
...
coro_t c( f);
// pass argument
c( str);
// returned value
int res = c.get();
The __coro_fn__ is started at __coro__ construction (similar to __thread__)
in a newly created __coro__ complete with registers, flags, stack and
instruction pointer.
If __coro_fn__ requires some arguments (types defined by __signature__)
on start-up those parameters must be applied to the __coro__ constructor.
A single arguments can be passed as it is:
typedef boost::coroutines::coroutine< int( std::string const&) > coro_t;
// void f( boost::coroutine< std::string const&( int) > & ca)
void f( coro_t::caller_type & ca);
std::string str("abc");
coro_t c( f, str);
For multiple arguments __args__ must be used (it is a typedef of __tuple__):
typedef boost::coroutines::coroutine< int( int, std::string const&) > coro_t;
// void f( boost::coroutine< boost::tuple< int, std::string const& >( int) > & ca)
void f( coro_t::caller_type & ca);
std::string str("abc");
coro_t c( f, coro_t::arguments( 7, str) );
[note The maximum number of arguments is limited to 10 (limit defined by
__boost_tuple__).]
[note Parameters bound with __bind__ to __coro_fn__ will not be part of the
__coro_op__ signature.]
__attrs__, an additional constructor argument of __coro__, defines the stack
size, stack unwinding and floating-point preserving behaviour used for __ctx__
construction.
The __coro__ constructor uses the __stack_allocator_concept__ to allocate an
associated stack, and the destructor uses the same __stack_allocator_concept__
to deallocate the stack. The default __stack_allocator_concept__ is
__stack_allocator__, but a custom stack-allocator can be passed to the
constructor.
[heading Calling a coroutine]
The execution control is transferred to __coro__ at construction (__coro_fn__
entered) - when control should be returned to the original calling routine,
invoke __coro_op__ on the first argument of type __coro_caller__ inside
__coro_fn__. __coro_caller__ is a typedef of __coro__ with an inverted
__signature__. Inverted __signature__ means that the return type becomes an
argument and vice versa. Multiple arguments are wrapped into __tuple__.
void f( boost::coroutines::coroutine< std::string const&( int) & ca);
boost::coroutines::coroutine< int( std::string const&) > c1( f);
void g( boost::coroutines::coroutine< boost::tuple< X, Y >( int) & ca);
boost::coroutines::coroutine< int( X, X) > c2( g);
The current coroutine information (registers, flags, and stack and instruction
pointer) is saved and the original context information is restored. Calling
__coro_op__ resumes execution in the coroutine after saving the new state of the
original routine.
typedef boost::coroutines::coroutine< void() > coro_t;
// void fn( boost::coroutines::coroutine< void() > & ca, int j)
void fn( coro_t::caller_type & ca, int j)
{
for( int i = 0; i < j; ++i)
{
std::cout << "fn(): local variable i == " << i << std::endl;
// save current coroutine
// value of local variable is preserved
// transfer execution control back to main()
ca();
// coroutine<>::operator()() was called
// execution control transferred back from main()
}
}
int main( int argc, char * argv[])
{
// bind parameter '7' to coroutine-fn
coro_t c( boost::bind( fn, _1, 7) );
std::cout << "main() starts coroutine c" << std::endl;
while ( c)
{
std::cout << "main() calls coroutine c" << std::endl;
// execution control is transferred to c
c();
}
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}
output:
main() starts coroutine c
fn(): local variable i == 0
main() calls coroutine c
fn(): local variable i == 1
main() calls coroutine c
fn(): local variable i == 2
main() calls coroutine c
fn(): local variable i == 3
main() calls coroutine c
fn(): local variable i == 4
main() calls coroutine c
fn(): local variable i == 5
main() calls coroutine c
fn(): local variable i == 6
main() calls coroutine c
Done
[warning Calling __coro_op__ from inside the [_same] coroutine results in
undefined behaviour.]
[heading Transfer of data]
__signature__, the template argument of __coro__, defines the types transferred
to and returned from the __coro_fn__, e.g. it determines the signature of
__coro_op__ and the return-type of __coro_get__.
[note __coro_caller__ is not part of __signature__ and __coro_fn__ is required
to return void and accept __coro_caller__ as argument.]
__coro_op__ accepts arguments as defined in __signature__ and returns a
reference to __coro__. The arguments passed to __coro_op__, in one coroutine,
is returned (as a __tuple__) by __coro_get__ in the other coroutine.
If __coro__ is constructed and arguments are passed to the constructor, the
__coro_fn__ will be entered and the arguments are accessed thorough __coro_get__
in __coro_fn__ on entry.
The value given to __coro_op__ of __coro_caller__, in one coroutine, is returned by
__coro_get__ in the other routine.
typedef boost::coroutines::coroutine< int( int) > coro_t;
// void fn( boost::coroutines::coroutine< int( int) > & ca)
void fn( coro_t::caller_type & ca)
{
// access the integer argument given to coroutine ctor
int i = ca.get();
std::cout << "fn(): local variable i == " << i << std::endl;
// save current coroutine context and
// transfer execution control back to caller
// pass content of variable 'i' to caller
// after execution control is returned back coroutine<>::operator()
// returns and the transferred integer s accessed via coroutine<>::get()
i = ca( i).get();
// i == 10 because c( 10) in main()
std::cout << "fn(): local variable i == " << i << std::endl;
ca( i);
}
int main( int argc, char * argv[])
{
std::cout << "main(): call coroutine c" << std::endl;
coro_t c( fn, 7);
int x = c.get();
std::cout << "main(): transferred value: " << x << std::endl;
x = c( 10).get();
std::cout << "main(): transferred value: " << x << std::endl;
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}
output:
main(): call coroutine c
fn(): local variable i == 7
main(): transferred value: 7
fn(): local variable i == 10
main(): transferred value: 10
Done
[heading __coro_fn__ with multiple arguments]
If __coro_fn__ has more than one argument __coro_op__ has the same size of
arguments and __coro_get__ from __coro_caller__ returns a __tuple__ corresponding
to the arguments of __signature__. __tie__ helps to access the values stored in
the __tuple__ returned by __coro_get__.
typedef boost::coroutines::coroutine< int(int,int) > coro_t;
// void fn( boost::coroutines::coroutine< boost::tuple< int, int >( int) > & ca)
void fn( coro_t::caller_type & ca)
{
int a, b;
boost::tie( a, b) = ca.get();
boost::tie( a, b) = ca( a + b).get();
ca( a + b);
}
int main( int argc, char * argv[])
{
std::cout << "main(): call coroutine c" << std::endl;
coro_t coro( fn, coro_t::arguments( 3, 7) );
int res = coro.get();
std::cout << "main(): 3 + 7 == " << res << std::endl;
res = coro( 5, 7).get();
std::cout << "main(): 5 + 7 == " << res << std::endl;
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}
output:
main(): call coroutine c
main(): 3 + 7 == 10
main(): 5 + 7 == 12
Done
[heading Transfer of pointers and references]
You can transfer references and pointers from and to coroutines but as usual
you must take care (scope, no re-assignment of const references etc.).
In the following code `x` points to `local` which is allocated on stack of
`c`. When `c` goes out of scope the stack becomes deallocated. Using `x`
after `c` is gone will fail!
struct X
{
void g();
};
typedef boost::coroutines::coroutine< X*() > coro_t;
// void fn( boost::coroutines::coroutine< void( X*) > & ca)
void fn( coro_t::caller_t & ca) {
X local;
ca( & local);
}
int main() {
X * x = 0;
{
coro_t c( fn);
x = c.get(); // let x point to X on stack owned by c
// stack gets unwound -> X will be destructed
}
x->g(); // segmentation fault!
return EXIT_SUCCESS;
}
[heading Range iterators]
__boost_coroutine__ provides output- and input-iterators using __boost_range__.
`coroutine< T() >` can be used via output-iterators using __begin__ and __end__.
typedef boost::coroutines::coroutine< int() > coro_t;
typedef boost::range_iterator< coro_t >::type iterator_t;
// void power( boost::coroutines::coroutine< void( int) > & ca, int number, int exponent)
void power( coro_t::caller_type & ca, int number, int exponent)
{
int counter = 0;
int result = 1;
while ( counter++ < exponent)
{
result = result * number;
ca( result);
}
}
int main()
{
coro_t c( boost::bind( power, _1, 2, 8) );
iterator_t e( boost::end( c) );
for ( iterator_t i( boost::begin( c) ); i != e; ++i)
std::cout << * i << " ";
std::cout << "\nDone" << std::endl;
return EXIT_SUCCESS;
}
output:
2 4 8 16 32 64 128 256
Done
`BOOST_FOREACH` can be used to iterate over the coroutine range too.
typedef boost::coroutines::coroutine< int() > coro_t;
typedef boost::range_iterator< coro_t >::type iterator_t;
// void power( boost::coroutines::coroutine< void( int) > & ca, int number, int exponent)
void power( coro_t::caller_type & ca, int number, int exponent)
{
int counter = 0;
int result = 1;
while ( counter++ < exponent)
{
result = result * number;
ca( result);
}
}
int main()
{
coro_t c( boost::bind( power, _1, 2, 8) );
BOOST_FOREACH( int i, c)
{ std::cout << i << " "; }
std::cout << "\nDone" << std::endl;
return EXIT_SUCCESS;
}
output:
2 4 8 16 32 64 128 256
Done
Input iterators are created from coroutines of type `coroutine< void( T) >`.
[heading Exit a __coro_fn__]
__coro_fn__ is exited with a simple return statement jumping back to the calling
routine. The __coro__ becomes complete, e.g. __coro_bool__ will return 'false'.
typedef boost::coroutines::coroutine< int(int,int) > coro_t;
// void power( boost::coroutines::coroutine< boost::tuple< int, int >( int) > & ca, int number, int exponent)
void fn( coro_t::caller_type & ca)
{
int a, b;
boost::tie( a, b) = ca.get();
boost::tie( a, b) = ca( a + b).get();
ca( a + b);
}
int main( int argc, char * argv[])
{
std::cout << "main(): call coroutine c" << std::endl;
coro_t coro( fn, coro_t::arguments( 3, 7) );
BOOST_ASSERT( coro);
int res = coro.get();
std::cout << "main(): 3 + 7 == " << res << std::endl;
res = coro( 5, 7).get();
BOOST_ASSERT( ! coro);
std::cout << "main(): 5 + 7 == " << res << std::endl;
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}
output:
main(): call coroutine c
main(): 3 + 7 == 10
main(): 5 + 7 == 12
Done
[important After returning from __coro_fn__ the __coro__ is complete (can not
resumed with __coro_op__).]
[heading Exceptions in __coro_fn__]
An exception thrown inside __coro_fn__ will transferred via exception-pointer
(see __boost_exception__ for details) and re-thrown by constructor or
__coro_op__.
typedef boost::coroutines::coroutine< void() > coro_t;
// void fn( boost::coroutines::coroutine< void() > & ca)
void fn( coro_t::caller_type & ca)
{
ca();
throw std::runtime_error("abc");
}
int main( int argc, char * argv[])
{
coro_t c( f);
try
{
c();
}
catch ( std::exception const& e)
{
std::cout << "exception catched:" << e.what() << std::endl;
return EXIT_FAILURE;
}
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}
output:
exception catched: abc
[important Code executed by coroutine 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 the
pending exception.]
try
{
// code that might throw
}
catch( forced_unwind)
{
throw;
}
catch(...)
{
// possibly not re-throw pending exception
}
[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 third argument of the coroutine constructor, `do_unwind`,
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.
typedef boost::coroutines::coroutine< void() > coro_t;
struct X
{
X()
{ std::cout << "X()" << std::endl; }
~X()
{ std::cout << "~X()" << std::endl; }
};
// void fn( boost::coroutines::coroutine< void() > & ca)
void fn( coro_t::caller_type & ca)
{
X x;
for ( int = 0;; ++i)
{
std::cout << "fn(): " << i << std::endl;
// transfer execution control back to main()
ca();
}
}
int main( int argc, char * argv[])
{
{
coro_t c( fn,
boost::coroutines::attributes(
boost::ctx::default_stacksize(),
boost::coroutines::stack_unwind) );
c();
c();
c();
c();
c();
std::cout << "c is complete: " << std::boolalpha << c.is_complete() << "\n";
}
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}
output:
X()
fn(): 0
fn(): 1
fn(): 2
fn(): 3
fn(): 4
fn(): 5
c is complete: false
~X()
Done
[important You must not swallow __forced_unwind__ exceptions!]
[heading FPU preserving]
Some applications do not use floating-point registers and can disable preserving
fpu registers for performance reasons.
[note According to the calling convention the FPU registers are preserved by default.]
[section:coroutine Class `coroutine`]
#include <boost/coroutine/coroutine.hpp>
template< typename Signature >
class coroutine;
template<
typename R,
typename ArgTypes...
>
class coroutine< R ( ArgTypes...) >
{
public:
typedef unspec-type caller_type;
typedef unspec-type arguments;
coroutine();
template<
typename Fn,
typename StackAllocator = stack_allocator,
typename Allocator = std::allocator< coroutine >
>
coroutine( Fn fn, attributes const& attr = attributes(),
StackAllocator const& stack_alloc = StackAllocator(),
Allocator const& alloc = Allocator() );
template<
typename Fn,
typename StackAllocator = stack_allocator,
typename Allocator = std::allocator< coroutine >
>
coroutine( Fn fn, arguments const& args,
attributes const& attr = attributes(),
StackAllocator const& stack_alloc = StackAllocator(),
Allocator const& alloc = Allocator() );
template<
typename Fn,
typename StackAllocator = stack_allocator,
typename Allocator = std::allocator< coroutine >
>
coroutine( Fn && fn, attributes const& attr = attributes(),
StackAllocator stack_alloc = StackAllocator(),
Allocator const& alloc = Allocator() );
template<
typename Fn,
typename StackAllocator = stack_allocator,
typename Allocator = std::allocator< coroutine >
>
coroutine( Fn && fn arguments const& args,
attributes const& attr = attributes(),
StackAllocator stack_alloc = StackAllocator(),
Allocator const& alloc = Allocator() );
coroutine( coroutine && other);
coroutine & operator=( coroutine && other);
operator unspecified-bool-type() const;
bool operator!() const;
void swap( coroutine & other);
bool empty() const;
coroutine & operator()(A0 a0, ..., A9 a9);
R get() const;
};
template< typename Signature >
void swap( coroutine< Signature > & l, coroutine< Signature > & r);
template< typename T >
range_iterator< coroutine< T() > >::type begin( coroutine< T() > &);
template< typename T >
range_iterator< coroutine< void(T) > >::type begin( coroutine< void(T) > &);
template< typename T >
range_iterator< coroutine< T() > >::type end( coroutine< T() > &);
template< typename T >
range_iterator< coroutine< void(T) > >::type end( coroutine< void(T) > &);
[heading `coroutine()`]
[variablelist
[[Effects:] [Creates a coroutine representing __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `template< typename Fn, typename StackAllocator, typename Allocator >
coroutine( Fn fn, attributes const& attr, StackAllocator const& stack_alloc, Allocator const& alloc)`]
[variablelist
[[Preconditions:] [`size` > minimum_stacksize(), `size` < maximum_stacksize()
when ! is_stack_unbound().]]
[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
determines stack clean-up and preserving floating-point registers.
For allocating/deallocating the stack `stack_alloc` is used and internal
data are allocated by Allocator.]]
]
[heading `template< typename Fn, typename StackAllocator, typename Allocator >
coroutine( Fn && fn, attributes const& attr, StackAllocator const& stack_alloc, Allocator const& alloc)`]
[variablelist
[[Preconditions:] [`size` > minimum_stacksize(), `size` < maximum_stacksize()
when ! is_stack_unbound().]]
[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
determines stack clean-up and preserving floating-point registers.
For allocating/deallocating the stack `stack_alloc` is used and internal
data are allocated by Allocator.]]
]
[heading `coroutine( coroutine && other)`]
[variablelist
[[Effects:] [Moves the internal data of `other` to `*this`.
`other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `coroutine & operator=( coroutine && 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 `operator unspecified-bool-type() const`]
[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`]
[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 `bool empty()`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__, the function returns true.
Otherwise false.]]
[[Throws:] [Nothing.]]
]
[heading `coroutine<> & operator()(A0 a0, A9 a9)`]
[variablelist
[[Preconditions:] [operator unspecified-bool-type() returns true for `*this`.]
[[Effects:] [Execution control is transferred to __coro_fn__ and the arguments
`a0`,..., are passed to the coroutine-function.]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]
[heading `R get()()`]
[variablelist
[[Preconditions:] [`*this` is not a __not_a_coro__, `! is_complete()`.]]
[[Returns:] [Returns data transferred from coroutine-function via __coro_op__
of __coro_caller__.]]
[[Throws:] [Nothing.]]
]
[heading `void swap( coroutine & other)`]
[variablelist
[[Effects:] [Swaps the internal data from `*this` with the values
of `other`.]]
[[Throws:] [Nothing.]]
]
[heading `T caller_type::operator()( R)`]
[variablelist
[[Effects:] [Gives execution control back to calling context by returning
a value of type R. The return type of this function is a __tuple__ containing
the arguments passed to __coro_op__.]]
[[Throws:] [Nothing.]]
]
[heading Non-member function `swap()`]
template< typename Signature >
void swap( coroutine< Signature > & l, coroutine< Signature > & r);
[variablelist
[[Effects:] [As if 'l.swap( r)'.]]
]
[heading Non-member function `begin( coroutine< T() > &)`]
template< typename T >
range_iterator< coroutine< T() > >::type begin( coroutine< T() > &);
[variablelist
[[Returns:] [Returns a range-iterator (input-iterator).]]
]
[heading Non-member function `begin( coroutine< void(T) > &)`]
template< typename T >
range_iterator< coroutine< void(T) > >::type begin( coroutine< void(T) > &);
[variablelist
[[Returns:] [Returns a range-iterator (output-iterator).]]
]
[heading Non-member function `end( coroutine< T() > &)`]
template< typename T >
range_iterator< coroutine< T() > >::type end( coroutine< T() > &);
[variablelist
[[Returns:] [Returns a end range-iterator (input-iterator).]]
]
[heading Non-member function `end( coroutine< void(T) > &)`]
template< typename T >
range_iterator< coroutine< void(T) > >::type end( coroutine< void(T) > &);
[variablelist
[[Returns:] [Returns a end range-iterator (output-iterator).]]
]
[endsect]
[endsect]