]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/coroutine/doc/asymmetric.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / coroutine / doc / asymmetric.qbk
CommitLineData
7c673cae
FG
1[/
2 Copyright Oliver Kowalke 2009.
3 Distributed under the Boost Software License, Version 1.0.
4 (See accompanying file LICENSE_1_0.txt or copy at
5 http://www.boost.org/LICENSE_1_0.txt
6]
7
8[section:asymmetric Asymmetric coroutine]
9
10Two asymmetric coroutine types - __push_coro__ and __pull_coro__ - provide a
11unidirectional transfer of data.
12
13
14[heading __pull_coro__]
15__pull_coro__ transfers data from another execution context (== pulled-from).
16The template parameter defines the transferred parameter type.
17The constructor of __pull_coro__ takes a function (__coro_fn__) accepting a
18reference to an __push_coro__ as argument. Instantiating an __pull_coro__ passes
19the control of execution to __coro_fn__ and a complementary __push_coro__ is
20synthesized by the library and passed as reference to __coro_fn__.
21
22This kind of coroutine provides __pull_coro_op__. This method only switches
23context; it transfers no data.
24
25__pull_coro__ provides input iterators (__pull_coro_it__) and __begin__/__end__
26are overloaded. The increment-operation switches the context and transfers data.
27
28 boost::coroutines::asymmetric_coroutine<int>::pull_type source(
29 [&](boost::coroutines::asymmetric_coroutine<int>::push_type& sink){
30 int first=1,second=1;
31 sink(first);
32 sink(second);
33 for(int i=0;i<8;++i){
34 int third=first+second;
35 first=second;
36 second=third;
37 sink(third);
38 }
39 });
40
41 for(auto i:source)
42 std::cout << i << " ";
43
44 output:
45 1 1 2 3 5 8 13 21 34 55
46
47In this example an __pull_coro__ is created in the main execution context taking
48a lambda function (== __coro_fn__) which calculates Fibonacci numbers in a
49simple ['for]-loop.
50The __coro_fn__ is executed in a newly created execution context which is
51managed by the instance of __pull_coro__.
52An __push_coro__ is automatically generated by the library and passed as
53reference to the lambda function. Each time the lambda function calls
54__push_coro_op__ with another Fibonacci number, __push_coro__ transfers it back
55to the main execution context. The local state of __coro_fn__ is preserved and
56will be restored upon transferring execution control back to __coro_fn__
57to calculate the next Fibonacci number.
58Because __pull_coro__ provides input iterators and __begin__/__end__ are
59overloaded, a ['range-based for]-loop can be used to iterate over the generated
60Fibonacci numbers.
61
62
63[heading __push_coro__]
64__push_coro__ transfers data to the other execution context (== pushed-to).
65The template parameter defines the transferred parameter type.
66The constructor of __push_coro__ takes a function (__coro_fn__) accepting a
67reference to an __pull_coro__ as argument. In contrast to __pull_coro__,
68instantiating an __push_coro__ does not pass the control of execution to
69__coro_fn__ - instead the first call of __push_coro_op__ synthesizes a
70complementary __pull_coro__ and passes it as reference to __coro_fn__.
71
72The __push_coro__ interface does not contain a ['get()]-function: you can not retrieve
73values from another execution context with this kind of coroutine.
74
75__push_coro__ provides output iterators (__push_coro_it__) and
76__begin__/__end__ are overloaded. The increment-operation switches the context
77and transfers data.
78
79 struct FinalEOL{
80 ~FinalEOL(){
81 std::cout << std::endl;
82 }
83 };
84
85 const int num=5, width=15;
86 boost::coroutines::asymmetric_coroutine<std::string>::push_type writer(
87 [&](boost::coroutines::asymmetric_coroutine<std::string>::pull_type& in){
88 // finish the last line when we leave by whatever means
89 FinalEOL eol;
90 // pull values from upstream, lay them out 'num' to a line
91 for (;;){
92 for(int i=0;i<num;++i){
93 // when we exhaust the input, stop
94 if(!in) return;
95 std::cout << std::setw(width) << in.get();
96 // now that we've handled this item, advance to next
97 in();
98 }
99 // after 'num' items, line break
100 std::cout << std::endl;
101 }
102 });
103
104 std::vector<std::string> words{
105 "peas", "porridge", "hot", "peas",
106 "porridge", "cold", "peas", "porridge",
107 "in", "the", "pot", "nine",
108 "days", "old" };
109
110 std::copy(boost::begin(words),boost::end(words),boost::begin(writer));
111
112 output:
113 peas porridge hot peas porridge
114 cold peas porridge in the
115 pot nine days old
116
117In this example an __push_coro__ is created in the main execution context
118accepting a lambda function (== __coro_fn__) which requests strings and lays out
119'num' of them on each line.
120This demonstrates the inversion of control permitted by coroutines. Without
121coroutines, a utility function to perform the same job would necessarily
122accept each new value as a function parameter, returning after processing that
123single value. That function would depend on a static state variable. A
124__coro_fn__, however, can request each new value as if by calling a function
125-- even though its caller also passes values as if by calling a function.
126The __coro_fn__ is executed in a newly created execution context which is
127managed by the instance of __push_coro__.
128The main execution context passes the strings to the __coro_fn__ by calling
129__push_coro_op__.
130An __pull_coro__ instance is automatically generated by the library and passed as
131reference to the lambda function. The __coro_fn__ accesses the strings passed
132from the main execution context by calling __pull_coro_get__ and lays those
133strings out on ['std::cout] according the parameters 'num' and 'width'.
134The local state of __coro_fn__ is preserved and will be restored after
135transferring execution control back to __coro_fn__.
136Because __push_coro__ provides output iterators and __begin__/__end__ are
137overloaded, the ['std::copy] algorithm can be used to iterate over the vector
138containing the strings and pass them one by one to the coroutine.
139
140
141[heading coroutine-function]
142The __coro_fn__ returns ['void] and takes its counterpart-coroutine as
143argument, so that using the coroutine passed as argument to __coro_fn__ is the
144only way to transfer data and execution control back to the caller.
145Both coroutine types take the same template argument.
146For __pull_coro__ the __coro_fn__ is entered at __pull_coro__ construction.
147For __push_coro__ the __coro_fn__ is not entered at __push_coro__ construction
148but entered by the first invocation of __push_coro_op__.
149After execution control is returned from __coro_fn__ the state of the
150coroutine can be checked via __pull_coro_bool__ returning `true` if the
151coroutine is still valid (__coro_fn__ has not terminated). Unless the first
152template parameter is `void`, `true` also implies that a data value is
153available.
154
155
156[heading passing data from a pull-coroutine to main-context]
157In order to transfer data from an __pull_coro__ to the main-context the framework
158synthesizes an __push_coro__ associated with the __pull_coro__ instance in the
159main-context. The synthesized __push_coro__ is passed as argument to __coro_fn__.
160The __coro_fn__ must call this __push_coro_op__ in order to transfer each
161data value back to the main-context.
162In the main-context, the __pull_coro_bool__ determines whether the coroutine is
163still valid and a data value is available or __coro_fn__ has terminated
164(__pull_coro__ is invalid; no data value available). Access to the transferred
165data value is given by __pull_coro_get__.
166
167 boost::coroutines::asymmetric_coroutine<int>::pull_type source( // constructor enters coroutine-function
168 [&](boost::coroutines::asymmetric_coroutine<int>::push_type& sink){
169 sink(1); // push {1} back to main-context
170 sink(1); // push {1} back to main-context
171 sink(2); // push {2} back to main-context
172 sink(3); // push {3} back to main-context
173 sink(5); // push {5} back to main-context
174 sink(8); // push {8} back to main-context
175 });
176
177 while(source){ // test if pull-coroutine is valid
178 int ret=source.get(); // access data value
179 source(); // context-switch to coroutine-function
180 }
181
182
183[heading passing data from main-context to a push-coroutine]
184In order to transfer data to an __push_coro__ from the main-context the framework
185synthesizes an __pull_coro__ associated with the __push_coro__ instance in the
186main-context. The synthesized __pull_coro__ is passed as argument to __coro_fn__.
187The main-context must call this __push_coro_op__ in order to transfer each data
188value into the __coro_fn__.
189Access to the transferred data value is given by __pull_coro_get__.
190
191 boost::coroutines::asymmetric_coroutine<int>::push_type sink( // constructor does NOT enter coroutine-function
192 [&](boost::coroutines::asymmetric_coroutine<int>::pull_type& source){
193 for (int i:source) {
194 std::cout << i << " ";
195 }
196 });
197
198 std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
199 for( int i:v){
200 sink(i); // push {i} to coroutine-function
201 }
202
203
204[heading accessing parameters]
205Parameters returned from or transferred to the __coro_fn__ can be accessed with
206__pull_coro_get__.
207
208Splitting-up the access of parameters from context switch function enables to
209check if __pull_coro__ is valid after return from __pull_coro_op__, e.g.
210__pull_coro__ has values and __coro_fn__ has not terminated.
211
212 boost::coroutines::asymmetric_coroutine<boost::tuple<int,int>>::push_type sink(
213 [&](boost::coroutines::asymmetric_coroutine<boost::tuple<int,int>>::pull_type& source){
214 // access tuple {7,11}; x==7 y==1
215 int x,y;
216 boost::tie(x,y)=source.get();
217 });
218
219 sink(boost::make_tuple(7,11));
220
221
222[heading exceptions]
223An exception thrown inside an __pull_coro__'s __coro_fn__ before its first call
224to __push_coro_op__ will be re-thrown by the __pull_coro__ constructor. After an
225__pull_coro__'s __coro_fn__'s first call to __push_coro_op__, any subsequent
226exception inside that __coro_fn__ will be re-thrown by __pull_coro_op__.
227__pull_coro_get__ does not throw.
228
229An exception thrown inside an __push_coro__'s __coro_fn__ will be re-thrown by
230__push_coro_op__.
231
232[important Code executed by __coro_fn__ must not prevent the propagation of the
233__forced_unwind__ exception. Absorbing that exception will cause stack
234unwinding to fail. Thus, any code that catches all exceptions must re-throw any
235pending __forced_unwind__ exception.]
236
237 try {
238 // code that might throw
239 } catch(const boost::coroutines::detail::forced_unwind&) {
240 throw;
241 } catch(...) {
242 // possibly not re-throw pending exception
243 }
244
245[important Do not jump from inside a catch block and then re-throw the
246exception in another execution context.]
247
248
249[heading Stack unwinding]
250Sometimes it is necessary to unwind the stack of an unfinished coroutine to
251destroy local stack variables so they can release allocated resources (RAII
252pattern). The `attributes` argument of the coroutine constructor
253indicates whether the destructor should unwind the stack (stack is unwound by
254default).
255
256Stack unwinding assumes the following preconditions:
257
258* The coroutine is not __not_a_coro__
259* The coroutine is not complete
260* The coroutine is not running
261* The coroutine owns a stack
262
263After unwinding, a __coro__ is complete.
264
265 struct X {
266 X(){
267 std::cout<<"X()"<<std::endl;
268 }
269
270 ~X(){
271 std::cout<<"~X()"<<std::endl;
272 }
273 };
274
275 {
276 boost::coroutines::asymmetric_coroutine<void>::push_type sink(
277 [&](boost::coroutines::asymmetric_coroutine<void>::pull_type& source){
278 X x;
279 for(int=0;;++i){
280 std::cout<<"fn(): "<<i<<std::endl;
281 // transfer execution control back to main()
282 source();
283 }
284 });
285
286 sink();
287 sink();
288 sink();
289 sink();
290 sink();
291
292 std::cout<<"sink is complete: "<<std::boolalpha<<!sink<<"\n";
293 }
294
295 output:
296 X()
297 fn(): 0
298 fn(): 1
299 fn(): 2
300 fn(): 3
301 fn(): 4
302 fn(): 5
303 sink is complete: false
304 ~X()
305
306
307[heading Range iterators]
308__boost_coroutine__ provides output- and input-iterators using __boost_range__.
309__pull_coro__ can be used via input-iterators using __begin__ and __end__.
310
311 int number=2,exponent=8;
312 boost::coroutines::asymmetric_coroutine< int >::pull_type source(
313 [&]( boost::coroutines::asymmetric_coroutine< int >::push_type & sink){
314 int counter=0,result=1;
315 while(counter++<exponent){
316 result=result*number;
317 sink(result);
318 }
319 });
320
321 for (auto i:source)
322 std::cout << i << " ";
323
324 output:
325 2 4 8 16 32 64 128 256
326
327['asymmetric_coroutine<>::pull_type::iterator::operator++()] corresponds to
328__pull_coro_op__; ['asymmetric_coroutine<>::pull_type::iterator::operator*()]
329roughly corresponds to __pull_coro_get__. An iterator originally obtained from
330__begin__ of an __pull_coro__ compares equal to an iterator obtained from
331__end__ of that same __pull_coro__ instance when its __pull_coro_bool__ would
332return `false`].
333
334[note If `T` is a move-only type, then
335['asymmetric_coroutine<T>::pull_type::iterator] may only be dereferenced once
336before it is incremented again.]
337
338Output-iterators can be created from __push_coro__.
339
340 boost::coroutines::asymmetric_coroutine<int>::push_type sink(
341 [&](boost::coroutines::asymmetric_coroutine<int>::pull_type& source){
342 while(source){
343 std::cout << source.get() << " ";
344 source();
345 }
346 });
347
348 std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
349 std::copy(boost::begin(v),boost::end(v),boost::begin(sink));
350
351['asymmetric_coroutine<>::push_type::iterator::operator*()] roughly
352corresponds to __push_coro_op__. An iterator originally obtained from
353__begin__ of an __push_coro__ compares equal to an iterator obtained from
354__end__ of that same __push_coro__ instance when its __push_coro_bool__ would
355return `false`.
356
357
358[heading Exit a __coro_fn__]
359__coro_fn__ is exited with a simple return statement jumping back to the calling
360routine. The __pull_coro__, __push_coro__ becomes complete, e.g. __pull_coro_bool__,
361__push_coro_bool__ will return `false`.
362
363[important After returning from __coro_fn__ the __coro__ is complete (can not
364resumed with __push_coro_op__, __pull_coro_op__).]
365
366
367
368[section:pull_coro Class `asymmetric_coroutine<>::pull_type`]
369
370 #include <boost/coroutine/asymmetric_coroutine.hpp>
371
372 template< typename R >
373 class asymmetric_coroutine<>::pull_type
374 {
375 public:
376 pull_type() noexcept;
377
378 template< typename Fn >
379 pull_type( Fn && fn, attributes const& attr = attributes() );
380
381 template< typename Fn, typename StackAllocator >
382 pull_type( Fn && fn, attributes const& attr, StackAllocator stack_alloc);
383
384 pull_type( pull_type const& other)=delete;
385
386 pull_type & operator=( pull_type const& other)=delete;
387
388 ~pull_type();
389
390 pull_type( pull_type && other) noexcept;
391
392 pull_type & operator=( pull_type && other) noexcept;
393
394 operator unspecified-bool-type() const noexcept;
395
396 bool operator!() const noexcept;
397
398 void swap( pull_type & other) noexcept;
399
400 pull_type & operator()();
401
402 R get() const;
403 };
404
405 template< typename R >
406 void swap( pull_type< R > & l, pull_type< R > & r);
407
408 template< typename R >
409 range_iterator< pull_type< R > >::type begin( pull_type< R > &);
410
411 template< typename R >
412 range_iterator< pull_type< R > >::type end( pull_type< R > &);
413
414[heading `pull_type()`]
415[variablelist
416[[Effects:] [Creates a coroutine representing __not_a_coro__.]]
417[[Throws:] [Nothing.]]
418]
419
420[heading `template< typename Fn >
421 pull_type( Fn && fn, attributes const& attr)`]
422[variablelist
423[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
424when ! is_stack_unbounded().]]
425[[Effects:] [Creates a coroutine which will execute `fn`, and enters it.
426Argument `attr` determines stack clean-up.]]
427[[Throws:] [Exceptions thrown inside __coro_fn__.]]
428]
429
430[heading `template< typename Fn, typename StackAllocator >
431 pull_type( Fn && fn, attributes const& attr, StackAllocator const& stack_alloc)`]
432[variablelist
433[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
434when ! is_stack_unbounded().]]
435[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
436determines stack clean-up.
437For allocating/deallocating the stack `stack_alloc` is used.]]
438[[Throws:] [Exceptions thrown inside __coro_fn__.]]
439]
440
441[heading `~pull_type()`]
442[variablelist
443[[Effects:] [Destroys the context and deallocates the stack.]]
444]
445
446[heading `pull_type( pull_type && other)`]
447[variablelist
448[[Effects:] [Moves the internal data of `other` to `*this`.
449`other` becomes __not_a_coro__.]]
450[[Throws:] [Nothing.]]
451]
452
453[heading `pull_type & operator=( pull_type && other)`]
454[variablelist
455[[Effects:] [Destroys the internal data of `*this` and moves the
456internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
457[[Throws:] [Nothing.]]
458]
459
460[heading `operator unspecified-bool-type() const`]
461[variablelist
462[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
463has returned (completed), the function returns `false`. Otherwise `true`.]]
464[[Throws:] [Nothing.]]
465]
466
467[heading `bool operator!() const`]
468[variablelist
469[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
470has returned (completed), the function returns `true`. Otherwise `false`.]]
471[[Throws:] [Nothing.]]
472]
473
474[heading `pull_type<> & operator()()`]
475[variablelist
476[[Preconditions:] [`*this` is not a __not_a_coro__.]]
477[[Effects:] [Execution control is transferred to __coro_fn__ (no parameter is
478passed to the coroutine-function).]]
479[[Throws:] [Exceptions thrown inside __coro_fn__.]]
480]
481
482[heading `R get()`]
483
484 R asymmetric_coroutine<R,StackAllocator>::pull_type::get();
485 R& asymmetric_coroutine<R&,StackAllocator>::pull_type::get();
486 void asymmetric_coroutine<void,StackAllocator>::pull_type::get()=delete;
487
488[variablelist
489[[Preconditions:] [`*this` is not a __not_a_coro__.]]
490[[Returns:] [Returns data transferred from coroutine-function via
491__push_coro_op__.]]
492[[Throws:] [`invalid_result`]]
493[[Note:] [If `R` is a move-only type, you may only call `get()` once before
494the next __pull_coro_op__ call.]]
495]
496
497[heading `void swap( pull_type & other)`]
498[variablelist
499[[Effects:] [Swaps the internal data from `*this` with the values
500of `other`.]]
501[[Throws:] [Nothing.]]
502]
503
504[heading Non-member function `swap()`]
505
506 template< typename R >
507 void swap( pull_type< R > & l, pull_type< R > & r);
508
509[variablelist
510[[Effects:] [As if 'l.swap( r)'.]]
511]
512
513[heading Non-member function `begin( pull_type< R > &)`]
514 template< typename R >
515 range_iterator< pull_type< R > >::type begin( pull_type< R > &);
516
517[variablelist
518[[Returns:] [Returns a range-iterator (input-iterator).]]
519]
520
521[heading Non-member function `end( pull_type< R > &)`]
522 template< typename R >
523 range_iterator< pull_type< R > >::type end( pull_type< R > &);
524
525[variablelist
526[[Returns:] [Returns an end range-iterator (input-iterator).]]
527[[Note:] [When first obtained from `begin( pull_type< R > &)`, or after some
528number of increment operations, an iterator will compare equal to the iterator
529returned by `end( pull_type< R > &)` when the corresponding __pull_coro_bool__
530would return `false`.]]
531]
532
533[endsect]
534
535
536[section:push_coro Class `asymmetric_coroutine<>::push_type`]
537
538 #include <boost/coroutine/asymmetric_coroutine.hpp>
539
540 template< typename Arg >
541 class asymmetric_coroutine<>::push_type
542 {
543 public:
544 push_type() noexcept;
545
546 template< typename Fn >
547 push_type( Fn && fn, attributes const& attr = attributes() );
548
549 template< typename Fn, typename StackAllocator >
550 push_type( Fn && fn, attributes const& attr, StackAllocator stack_alloc);
551
552 push_type( push_type const& other)=delete;
553
554 push_type & operator=( push_type const& other)=delete;
555
556 ~push_type();
557
558 push_type( push_type && other) noexcept;
559
560 push_type & operator=( push_type && other) noexcept;
561
562 operator unspecified-bool-type() const noexcept;
563
564 bool operator!() const noexcept;
565
566 void swap( push_type & other) noexcept;
567
568 push_type & operator()( Arg arg);
569 };
570
571 template< typename Arg >
572 void swap( push_type< Arg > & l, push_type< Arg > & r);
573
574 template< typename Arg >
575 range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);
576
577 template< typename Arg >
578 range_iterator< push_type< Arg > >::type end( push_type< Arg > &);
579
580[heading `push_type()`]
581[variablelist
582[[Effects:] [Creates a coroutine representing __not_a_coro__.]]
583[[Throws:] [Nothing.]]
584]
585
586[heading `template< typename Fn >
587 push_type( Fn && fn, attributes const& attr)`]
588[variablelist
589[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
590when ! is_stack_unbounded().]]
591[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
592determines stack clean-up.]]
593]
594
595[heading `template< typename Fn, typename StackAllocator >
596 push_type( Fn && fn, attributes const& attr, StackAllocator const& stack_alloc)`]
597[variablelist
598[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
599when ! is_stack_unbounded().]]
600[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
601determines stack clean-up.
602For allocating/deallocating the stack `stack_alloc` is used.]]
603]
604
605[heading `~push_type()`]
606[variablelist
607[[Effects:] [Destroys the context and deallocates the stack.]]
608]
609
610[heading `push_type( push_type && other)`]
611[variablelist
612[[Effects:] [Moves the internal data of `other` to `*this`.
613`other` becomes __not_a_coro__.]]
614[[Throws:] [Nothing.]]
615]
616
617[heading `push_type & operator=( push_type && other)`]
618[variablelist
619[[Effects:] [Destroys the internal data of `*this` and moves the
620internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
621[[Throws:] [Nothing.]]
622]
623
624[heading `operator unspecified-bool-type() const`]
625[variablelist
626[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
627has returned (completed), the function returns `false`. Otherwise `true`.]]
628[[Throws:] [Nothing.]]
629]
630
631[heading `bool operator!() const`]
632[variablelist
633[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
634has returned (completed), the function returns `true`. Otherwise `false`.]]
635[[Throws:] [Nothing.]]
636]
637
638[heading `push_type & operator()(Arg arg)`]
639
640 push_type& asymmetric_coroutine<Arg>::push_type::operator()(Arg);
641 push_type& asymmetric_coroutine<Arg&>::push_type::operator()(Arg&);
642 push_type& asymmetric_coroutine<void>::push_type::operator()();
643
644[variablelist
645[[Preconditions:] [operator unspecified-bool-type() returns `true` for `*this`.]]
646[[Effects:] [Execution control is transferred to __coro_fn__ and the argument
647`arg` is passed to the coroutine-function.]]
648[[Throws:] [Exceptions thrown inside __coro_fn__.]]
649]
650
651[heading `void swap( push_type & other)`]
652[variablelist
653[[Effects:] [Swaps the internal data from `*this` with the values
654of `other`.]]
655[[Throws:] [Nothing.]]
656]
657
658[heading Non-member function `swap()`]
659
660 template< typename Arg >
661 void swap( push_type< Arg > & l, push_type< Arg > & r);
662
663[variablelist
664[[Effects:] [As if 'l.swap( r)'.]]
665]
666
667[heading Non-member function `begin( push_type< Arg > &)`]
668 template< typename Arg >
669 range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);
670
671[variablelist
672[[Returns:] [Returns a range-iterator (output-iterator).]]
673]
674
675[heading Non-member function `end( push_type< Arg > &)`]
676 template< typename Arg >
677 range_iterator< push_type< Arg > >::type end( push_type< Arg > &);
678
679[variablelist
680[[Returns:] [Returns a end range-iterator (output-iterator).]]
681[[Note:] [When first obtained from `begin( push_type< R > &)`, or after some
682number of increment operations, an iterator will compare equal to the iterator
683returned by `end( push_type< R > &)` when the corresponding __push_coro_bool__
684would return `false`.]]
685]
686
687[endsect]
688
689
690
691[endsect]