libstdc++
generator
Go to the documentation of this file.
1// <generator> -*- C++ -*-
2
3// Copyright (C) 2023-2024 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25/** @file include/generator
26 * This is a Standard C++ Library header.
27 */
28
29#ifndef _GLIBCXX_GENERATOR
30#define _GLIBCXX_GENERATOR
31
32#include <ranges>
33#pragma GCC system_header
34
35#include <bits/c++config.h>
36
37#define __glibcxx_want_generator
38#include <bits/version.h>
39
40#ifdef __cpp_lib_generator // C++ >= 23 && __glibcxx_coroutine
41#include <new>
42#include <bits/move.h>
43#include <bits/ranges_util.h>
44#include <bits/elements_of.h>
45#include <bits/uses_allocator.h>
46#include <bits/exception_ptr.h>
47#include <cstddef>
48#include <cstdint>
49#include <cstring>
50#include <coroutine>
51
52#include <type_traits>
53#include <variant>
54#include <concepts>
55
56#if _GLIBCXX_HOSTED
57# include <bits/memory_resource.h>
58#endif // HOSTED
59
60namespace std _GLIBCXX_VISIBILITY(default)
61{
62_GLIBCXX_BEGIN_NAMESPACE_VERSION
63
64 /**
65 * @defgroup generator_coros Range generator coroutines
66 * @addtogroup ranges
67 * @since C++23
68 * @{
69 */
70
71 /** @brief A range specified using a yielding coroutine.
72 *
73 * `std::generator` is a utility class for defining ranges using coroutines
74 * that yield elements as a range. Generator coroutines are synchronous.
75 *
76 * @headerfile generator
77 * @since C++23
78 */
79 template<typename _Ref, typename _Val = void, typename _Alloc = void>
80 class generator;
81
82 /// @cond undocumented
83 namespace __gen
84 {
85 /// _Reference type for a generator whose reference (first argument) and
86 /// value (second argument) types are _Ref and _Val.
87 template<typename _Ref, typename _Val>
88 using _Reference_t = __conditional_t<is_void_v<_Val>,
89 _Ref&&, _Ref>;
90
91 /// Type yielded by a generator whose _Reference type is _Reference.
92 template<typename _Reference>
93 using _Yield_t = __conditional_t<is_reference_v<_Reference>,
94 _Reference,
95 const _Reference&>;
96
97 /// _Yield_t * _Reference_t
98 template<typename _Ref, typename _Val>
99 using _Yield2_t = _Yield_t<_Reference_t<_Ref, _Val>>;
100
101 template<typename> constexpr bool __is_generator = false;
102 template<typename _Val, typename _Ref, typename _Alloc>
103 constexpr bool __is_generator<std::generator<_Val, _Ref, _Alloc>> = true;
104
105 /// Allocator and value type erased generator promise type.
106 /// \tparam _Yielded The corresponding generators yielded type.
107 template<typename _Yielded>
108 class _Promise_erased
109 {
110 static_assert(is_reference_v<_Yielded>);
111 using _Yielded_deref = remove_reference_t<_Yielded>;
112 using _Yielded_decvref = remove_cvref_t<_Yielded>;
113 using _ValuePtr = add_pointer_t<_Yielded>;
114 using _Coro_handle = std::coroutine_handle<_Promise_erased>;
115
116 template<typename, typename, typename>
117 friend class std::generator;
118
119 template<typename _Gen>
120 struct _Recursive_awaiter;
121 template<typename>
122 friend struct _Recursive_awaiter;
123 struct _Copy_awaiter;
124 struct _Subyield_state;
125 struct _Final_awaiter;
126 public:
127 suspend_always
128 initial_suspend() const noexcept
129 { return {}; }
130
131 suspend_always
132 yield_value(_Yielded __val) noexcept
133 {
134 _M_bottom_value() = ::std::addressof(__val);
135 return {};
136 }
137
138 auto
139 yield_value(const _Yielded_deref& __val)
140 noexcept (is_nothrow_constructible_v<_Yielded_decvref,
141 const _Yielded_deref&>)
142 requires (is_rvalue_reference_v<_Yielded>
143 && constructible_from<_Yielded_decvref,
144 const _Yielded_deref&>)
145 { return _Copy_awaiter(__val, _M_bottom_value()); }
146
147 template<typename _R2, typename _V2, typename _A2, typename _U2>
148 requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded>
149 auto
150 yield_value(ranges::elements_of<generator<_R2, _V2, _A2>&&, _U2> __r)
151 noexcept
152 { return _Recursive_awaiter { std::move(__r.range) }; }
153
154 template<ranges::input_range _R, typename _Alloc>
155 requires convertible_to<ranges::range_reference_t<_R>, _Yielded>
156 auto
157 yield_value(ranges::elements_of<_R, _Alloc> __r)
158 {
159 auto __n = [] (allocator_arg_t, _Alloc,
160 ranges::iterator_t<_R> __i,
161 ranges::sentinel_t<_R> __s)
162 -> generator<_Yielded, ranges::range_value_t<_R>, _Alloc> {
163 for (; __i != __s; ++__i)
164 co_yield static_cast<_Yielded>(*__i);
165 };
166 return yield_value(ranges::elements_of(__n(allocator_arg,
167 __r.allocator,
168 ranges::begin(__r.range),
169 ranges::end(__r.range))));
170 }
171
172
173 _Final_awaiter
174 final_suspend() noexcept
175 { return {}; }
176
177 void
178 unhandled_exception()
179 {
180 // To get to this point, this coroutine must have been active. In that
181 // case, it must be the top of the stack. The current coroutine is
182 // the sole entry of the stack iff it is both the top and the bottom. As
183 // it is the top implicitly in this context it will be the sole entry iff
184 // it is the bottom.
185 if (_M_nest._M_is_bottom())
186 throw;
187 else
188 this->_M_except = std::current_exception();
189 }
190
191 void await_transform() = delete;
192 void return_void() const noexcept {}
193
194 private:
195 _ValuePtr&
196 _M_bottom_value() noexcept
197 { return _M_nest._M_bottom_value(*this); }
198
199 _ValuePtr&
200 _M_value() noexcept
201 { return _M_nest._M_value(*this); }
202
203 _Subyield_state _M_nest;
204 std::exception_ptr _M_except;
205 };
206
207 template<typename _Yielded>
208 struct _Promise_erased<_Yielded>::_Subyield_state
209 {
210 struct _Frame
211 {
212 _Coro_handle _M_bottom;
213 _Coro_handle _M_parent;
214 };
215
216 struct _Bottom_frame
217 {
218 _Coro_handle _M_top;
219 _ValuePtr _M_value = nullptr;
220 };
221
222 std::variant<
223 _Bottom_frame,
224 _Frame
225 > _M_stack;
226
227 bool
228 _M_is_bottom() const noexcept
229 { return !std::holds_alternative<_Frame>(this->_M_stack); }
230
231 _Coro_handle&
232 _M_top() noexcept
233 {
234 if (auto __f = std::get_if<_Frame>(&this->_M_stack))
235 return __f->_M_bottom.promise()._M_nest._M_top();
236
237 auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack);
238 __glibcxx_assert(__bf);
239 return __bf->_M_top;
240 }
241
242 void
243 _M_push(_Coro_handle __current, _Coro_handle __subyield) noexcept
244 {
245 __glibcxx_assert(&__current.promise()._M_nest == this);
246 __glibcxx_assert(this->_M_top() == __current);
247
248 __subyield.promise()._M_nest._M_jump_in(__current, __subyield);
249 }
250
251 std::coroutine_handle<>
252 _M_pop() noexcept
253 {
254 if (auto __f = std::get_if<_Frame>(&this->_M_stack))
255 {
256 // We aren't a bottom coroutine. Restore the parent to the top
257 // and resume.
258 auto __p = this->_M_top() = __f->_M_parent;
259 return __p;
260 }
261 else
262 // Otherwise, there's nothing to resume.
263 return std::noop_coroutine();
264 }
265
266 void
267 _M_jump_in(_Coro_handle __rest, _Coro_handle __new) noexcept
268 {
269 __glibcxx_assert(&__new.promise()._M_nest == this);
270 __glibcxx_assert(this->_M_is_bottom());
271 // We're bottom. We're also top if top is unset (note that this is
272 // not true if something was added to the coro stack and then popped,
273 // but in that case we can't possibly be yielded from, as it would
274 // require rerunning begin()).
275 __glibcxx_assert(!this->_M_top());
276
277 auto& __rn = __rest.promise()._M_nest;
278 __rn._M_top() = __new;
279
280 // Presume we're the second frame...
281 auto __bott = __rest;
282 if (auto __f = std::get_if<_Frame>(&__rn._M_stack))
283 // But, if we aren't, get the actual bottom. We're only the second
284 // frame if our parent is the bottom frame, i.e. it doesn't have a
285 // _Frame member.
286 __bott = __f->_M_bottom;
287
288 this->_M_stack = _Frame {
289 ._M_bottom = __bott,
290 ._M_parent = __rest
291 };
292 }
293
294 _ValuePtr&
295 _M_bottom_value(_Promise_erased& __current) noexcept
296 {
297 __glibcxx_assert(&__current._M_nest == this);
298 if (auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack))
299 return __bf->_M_value;
300 auto __f = std::get_if<_Frame>(&this->_M_stack);
301 __glibcxx_assert(__f);
302 auto& __p = __f->_M_bottom.promise();
303 return __p._M_nest._M_value(__p);
304 }
305
306 _ValuePtr&
307 _M_value(_Promise_erased& __current) noexcept
308 {
309 __glibcxx_assert(&__current._M_nest == this);
310 auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack);
311 __glibcxx_assert(__bf);
312 return __bf->_M_value;
313 }
314 };
315
316 template<typename _Yielded>
317 struct _Promise_erased<_Yielded>::_Final_awaiter
318 {
319 bool await_ready() noexcept
320 { return false; }
321
322 template<typename _Promise>
323 auto await_suspend(std::coroutine_handle<_Promise> __c) noexcept
324 {
325#ifdef __glibcxx_is_pointer_interconvertible
327 _Promise_erased, _Promise>);
328#endif
329
330 auto& __n = __c.promise()._M_nest;
331 return __n._M_pop();
332 }
333
334 void await_resume() noexcept {}
335 };
336
337 template<typename _Yielded>
338 struct _Promise_erased<_Yielded>::_Copy_awaiter
339 {
340 _Yielded_decvref _M_value;
341 _ValuePtr& _M_bottom_value;
342
343 constexpr bool await_ready() noexcept
344 { return false; }
345
346 template<typename _Promise>
347 void await_suspend(std::coroutine_handle<_Promise>) noexcept
348 {
349#ifdef __glibcxx_is_pointer_interconvertible
351 _Promise_erased, _Promise>);
352#endif
353 _M_bottom_value = ::std::addressof(_M_value);
354 }
355
356 constexpr void
357 await_resume() const noexcept
358 {}
359 };
360
361 template<typename _Yielded>
362 template<typename _Gen>
363 struct _Promise_erased<_Yielded>::_Recursive_awaiter
364 {
365 _Gen _M_gen;
366 static_assert(__is_generator<_Gen>);
367 static_assert(std::same_as<typename _Gen::yielded, _Yielded>);
368
369 _Recursive_awaiter(_Gen __gen) noexcept
370 : _M_gen(std::move(__gen))
371 { this->_M_gen._M_mark_as_started(); }
372
373 constexpr bool
374 await_ready() const noexcept
375 { return false; }
376
377
378 template<typename _Promise>
379 std::coroutine_handle<>
380 await_suspend(std::coroutine_handle<_Promise> __p) noexcept
381 {
382#ifdef __glibcxx_is_pointer_interconvertible
384 _Promise_erased, _Promise>);
385#endif
386
387 auto __c = _Coro_handle::from_address(__p.address());
388 auto __t = _Coro_handle::from_address(this->_M_gen._M_coro.address());
389 __p.promise()._M_nest._M_push(__c, __t);
390 return __t;
391 }
392
393 void await_resume()
394 {
395 if (auto __e = _M_gen._M_coro.promise()._M_except)
397 }
398 };
399
400 struct _Alloc_block
401 {
402 alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
403 char _M_data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
404
405 static auto
406 _M_cnt(std::size_t __sz) noexcept
407 {
408 auto __blksz = sizeof(_Alloc_block);
409 return (__sz + __blksz - 1) / __blksz;
410 }
411 };
412
413 template<typename _All>
414 concept _Stateless_alloc = (allocator_traits<_All>::is_always_equal::value
415 && default_initializable<_All>);
416
417 template<typename _Alloc>
418 class _Promise_alloc
419 {
420 using _ATr = allocator_traits<_Alloc>;
421 using _Rebound = typename _ATr::template rebind_alloc<_Alloc_block>;
422 using _Rebound_ATr = typename _ATr
423 ::template rebind_traits<_Alloc_block>;
424 static_assert(is_pointer_v<typename _Rebound_ATr::pointer>,
425 "Must use allocators for true pointers with generators");
426
427 static auto
428 _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
429 {
430 auto __an = __fn + __fsz;
431 auto __ba = alignof(_Rebound);
432 return reinterpret_cast<_Rebound*>(((__an + __ba - 1) / __ba) * __ba);
433 }
434
435 static auto
436 _M_alloc_size(std::size_t __csz) noexcept
437 {
438 auto __ba = alignof(_Rebound);
439 // Our desired layout is placing the coroutine frame, then pad out to
440 // align, then place the allocator. The total size of that is the
441 // size of the coroutine frame, plus up to __ba bytes, plus the size
442 // of the allocator.
443 return __csz + __ba + sizeof(_Rebound);
444 }
445
446 static void*
447 _M_allocate(_Rebound __b, std::size_t __csz)
448 {
449 if constexpr (_Stateless_alloc<_Rebound>)
450 // Only need room for the coroutine.
451 return __b.allocate(_Alloc_block::_M_cnt(__csz));
452 else
453 {
454 auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz));
455 auto __f = __b.allocate(__nsz);
456 auto __fn = reinterpret_cast<std::uintptr_t>(__f);
457 auto __an = _M_alloc_address(__fn, __csz);
458 ::new (__an) _Rebound(std::move(__b));
459 return __f;
460 }
461 }
462
463 public:
464 void*
465 operator new(std::size_t __sz)
466 requires default_initializable<_Rebound> // _Alloc is non-void
467 { return _M_allocate({}, __sz); }
468
469 template<typename _Na, typename... _Args>
470 void*
471 operator new(std::size_t __sz,
472 allocator_arg_t, const _Na& __na,
473 const _Args&...)
474 requires convertible_to<const _Na&, _Alloc>
475 {
476 return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)),
477 __sz);
478 }
479
480 template<typename _This, typename _Na, typename... _Args>
481 void*
482 operator new(std::size_t __sz,
483 const _This&,
484 allocator_arg_t, const _Na& __na,
485 const _Args&...)
486 requires convertible_to<const _Na&, _Alloc>
487 {
488 return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)),
489 __sz);
490 }
491
492 void
493 operator delete(void* __ptr, std::size_t __csz) noexcept
494 {
495 if constexpr (_Stateless_alloc<_Rebound>)
496 {
497 _Rebound __b;
498 return __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr),
499 _Alloc_block::_M_cnt(__csz));
500 }
501 else
502 {
503 auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz));
504 auto __fn = reinterpret_cast<std::uintptr_t>(__ptr);
505 auto __an = _M_alloc_address(__fn, __csz);
506 _Rebound __b(std::move(*__an));
507 __an->~_Rebound();
508 __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nsz);
509 }
510 }
511 };
512
513 template<>
514 class _Promise_alloc<void>
515 {
516 using _Dealloc_fn = void (*)(void*, std::size_t);
517
518 static auto
519 _M_dealloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
520 {
521 auto __an = __fn + __fsz;
522 auto __ba = alignof(_Dealloc_fn);
523 auto __aligned = ((__an + __ba - 1) / __ba) * __ba;
524 return reinterpret_cast<_Dealloc_fn*>(__aligned);
525 }
526
527 template<typename _Rebound>
528 static auto
529 _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
530 requires (!_Stateless_alloc<_Rebound>)
531 {
532 auto __ba = alignof(_Rebound);
533 auto __da = _M_dealloc_address(__fn, __fsz);
534 auto __aan = reinterpret_cast<std::uintptr_t>(__da);
535 __aan += sizeof(_Dealloc_fn);
536 auto __aligned = ((__aan + __ba - 1) / __ba) * __ba;
537 return reinterpret_cast<_Rebound*>(__aligned);
538 }
539
540 template<typename _Rebound>
541 static auto
542 _M_alloc_size(std::size_t __csz) noexcept
543 {
544 // This time, we want the coroutine frame, then the deallocator
545 // pointer, then the allocator itself, if any.
546 std::size_t __aa = 0;
547 std::size_t __as = 0;
548 if constexpr (!std::same_as<_Rebound, void>)
549 {
550 __aa = alignof(_Rebound);
551 __as = sizeof(_Rebound);
552 }
553 auto __ba = __aa + alignof(_Dealloc_fn);
554 return __csz + __ba + __as + sizeof(_Dealloc_fn);
555 }
556
557 template<typename _Rebound>
558 static void
559 _M_deallocator(void* __ptr, std::size_t __csz) noexcept
560 {
561 auto __asz = _M_alloc_size<_Rebound>(__csz);
562 auto __nblk = _Alloc_block::_M_cnt(__asz);
563
564 if constexpr (_Stateless_alloc<_Rebound>)
565 {
566 _Rebound __b;
567 __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk);
568 }
569 else
570 {
571 auto __fn = reinterpret_cast<std::uintptr_t>(__ptr);
572 auto __an = _M_alloc_address<_Rebound>(__fn, __csz);
573 _Rebound __b(std::move(*__an));
574 __an->~_Rebound();
575 __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk);
576 }
577 }
578
579 template<typename _Na>
580 static void*
581 _M_allocate(const _Na& __na, std::size_t __csz)
582 {
583 using _Rebound = typename std::allocator_traits<_Na>
584 ::template rebind_alloc<_Alloc_block>;
585 using _Rebound_ATr = typename std::allocator_traits<_Na>
586 ::template rebind_traits<_Alloc_block>;
587
588 static_assert(is_pointer_v<typename _Rebound_ATr::pointer>,
589 "Must use allocators for true pointers with generators");
590
591 _Dealloc_fn __d = &_M_deallocator<_Rebound>;
592 auto __b = static_cast<_Rebound>(__na);
593 auto __asz = _M_alloc_size<_Rebound>(__csz);
594 auto __nblk = _Alloc_block::_M_cnt(__asz);
595 void* __p = __b.allocate(__nblk);
596 auto __pn = reinterpret_cast<std::uintptr_t>(__p);
597 *_M_dealloc_address(__pn, __csz) = __d;
598 if constexpr (!_Stateless_alloc<_Rebound>)
599 {
600 auto __an = _M_alloc_address<_Rebound>(__pn, __csz);
601 ::new (__an) _Rebound(std::move(__b));
602 }
603 return __p;
604 }
605 public:
606 void*
607 operator new(std::size_t __sz)
608 {
609 auto __nsz = _M_alloc_size<void>(__sz);
610 _Dealloc_fn __d = [] (void* __ptr, std::size_t __sz)
611 {
612 ::operator delete(__ptr, _M_alloc_size<void>(__sz));
613 };
614 auto __p = ::operator new(__nsz);
615 auto __pn = reinterpret_cast<uintptr_t>(__p);
616 *_M_dealloc_address(__pn, __sz) = __d;
617 return __p;
618 }
619
620 template<typename _Na, typename... _Args>
621 void*
622 operator new(std::size_t __sz,
623 allocator_arg_t, const _Na& __na,
624 const _Args&...)
625 { return _M_allocate(__na, __sz); }
626
627 template<typename _This, typename _Na, typename... _Args>
628 void*
629 operator new(std::size_t __sz,
630 const _This&,
631 allocator_arg_t, const _Na& __na,
632 const _Args&...)
633 { return _M_allocate(__na, __sz); }
634
635 void
636 operator delete(void* __ptr, std::size_t __sz) noexcept
637 {
638 _Dealloc_fn __d;
639 auto __pn = reinterpret_cast<uintptr_t>(__ptr);
640 __d = *_M_dealloc_address(__pn, __sz);
641 __d(__ptr, __sz);
642 }
643 };
644
645 template<typename _Tp>
646 concept _Cv_unqualified_object = is_object_v<_Tp>
647 && same_as<_Tp, remove_cv_t<_Tp>>;
648 } // namespace __gen
649 /// @endcond
650
651 template<typename _Ref, typename _Val, typename _Alloc>
652 class generator
653 : public ranges::view_interface<generator<_Ref, _Val, _Alloc>>
654 {
655 using _Value = __conditional_t<is_void_v<_Val>,
656 remove_cvref_t<_Ref>,
657 _Val>;
658 static_assert(__gen::_Cv_unqualified_object<_Value>,
659 "Generator value must be a cv-unqualified object type");
660 using _Reference = __gen::_Reference_t<_Ref, _Val>;
661 static_assert(is_reference_v<_Reference>
662 || (__gen::_Cv_unqualified_object<_Reference>
663 && copy_constructible<_Reference>),
664 "Generator reference type must be either a cv-unqualified "
665 "object type that is trivially constructible or a "
666 "reference type");
667
668 using _RRef = __conditional_t<
669 is_reference_v<_Reference>,
670 remove_reference_t<_Reference>&&,
671 _Reference>;
672
673 /* Required to model indirectly_readable, and input_iterator. */
674 static_assert(common_reference_with<_Reference&&, _Value&&>);
675 static_assert(common_reference_with<_Reference&&, _RRef&&>);
676 static_assert(common_reference_with<_RRef&&, const _Value&>);
677
678 using _Yielded = __gen::_Yield_t<_Reference>;
679 using _Erased_promise = __gen::_Promise_erased<_Yielded>;
680
681 struct _Iterator;
682
683 friend _Erased_promise;
684 friend struct _Erased_promise::_Subyield_state;
685 public:
686 using yielded = _Yielded;
687
688 struct promise_type : _Erased_promise, __gen::_Promise_alloc<_Alloc>
689 {
690 generator get_return_object() noexcept
691 { return { coroutine_handle<promise_type>::from_promise(*this) }; }
692 };
693
694#ifdef __glibcxx_is_pointer_interconvertible
695 static_assert(is_pointer_interconvertible_base_of_v<_Erased_promise,
696 promise_type>);
697#endif
698
699 generator(const generator&) = delete;
700
701 generator(generator&& __other) noexcept
702 : _M_coro(std::__exchange(__other._M_coro, nullptr)),
703 _M_began(std::__exchange(__other._M_began, false))
704 {}
705
706 ~generator()
707 {
708 if (auto& __c = this->_M_coro)
709 __c.destroy();
710 }
711
712 generator&
713 operator=(generator __other) noexcept
714 {
715 swap(__other._M_coro, this->_M_coro);
716 swap(__other._M_began, this->_M_began);
717 }
718
719 _Iterator
720 begin()
721 {
722 this->_M_mark_as_started();
723 auto __h = _Coro_handle::from_promise(_M_coro.promise());
724 __h.promise()._M_nest._M_top() = __h;
725 return { __h };
726 }
727
728 default_sentinel_t
729 end() const noexcept
730 { return default_sentinel; }
731
732 private:
733 using _Coro_handle = std::coroutine_handle<_Erased_promise>;
734
735 generator(coroutine_handle<promise_type> __coro) noexcept
736 : _M_coro { move(__coro) }
737 {}
738
739 void
740 _M_mark_as_started() noexcept
741 {
742 __glibcxx_assert(!this->_M_began);
743 this->_M_began = true;
744 }
745
746 coroutine_handle<promise_type> _M_coro;
747 bool _M_began = false;
748 };
749
750 template<class _Ref, class _Val, class _Alloc>
751 struct generator<_Ref, _Val, _Alloc>::_Iterator
752 {
753 using value_type = _Value;
754 using difference_type = ptrdiff_t;
755
756 friend bool
757 operator==(const _Iterator& __i, default_sentinel_t) noexcept
758 { return __i._M_coro.done(); }
759
760 friend class generator;
761
762 _Iterator(_Iterator&& __o) noexcept
763 : _M_coro(std::__exchange(__o._M_coro, {}))
764 {}
765
766 _Iterator&
767 operator=(_Iterator&& __o) noexcept
768 {
769 this->_M_coro = std::__exchange(__o._M_coro, {});
770 return *this;
771 }
772
773 _Iterator&
774 operator++()
775 {
776 _M_next();
777 return *this;
778 }
779
780 void
781 operator++(int)
782 { this->operator++(); }
783
784 _Reference
785 operator*()
786 const noexcept(is_nothrow_move_constructible_v<_Reference>)
787 {
788 auto& __p = this->_M_coro.promise();
789 return static_cast<_Reference>(*__p._M_value());
790 }
791
792 private:
793 friend class generator;
794
795 _Iterator(_Coro_handle __g)
796 : _M_coro { __g }
797 { this->_M_next(); }
798
799 void _M_next()
800 {
801 auto& __t = this->_M_coro.promise()._M_nest._M_top();
802 __t.resume();
803 }
804
805 _Coro_handle _M_coro;
806 };
807
808 /// @}
809
810#if _GLIBCXX_HOSTED
811 namespace pmr {
812 template<typename _Ref, typename _Val = void>
813 using generator = std::generator<_Ref, _Val, polymorphic_allocator<std::byte>>;
814 }
815#endif // HOSTED
816
817_GLIBCXX_END_NAMESPACE_VERSION
818} // namespace std
819#endif // __cpp_lib_generator
820
821#endif // _GLIBCXX_GENERATOR
constexpr complex< _Tp > operator*(const complex< _Tp > &__x, const complex< _Tp > &__y)
Return new complex value x times y.
Definition complex:400
constexpr bool is_pointer_interconvertible_base_of_v
Definition type_traits:3849
constexpr _Tp * addressof(_Tp &__r) noexcept
Returns the actual address of the object or function referenced by r, even in the presence of an over...
Definition move.h:163
constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept
Convert a value to an rvalue.
Definition move.h:127
_Tp * end(valarray< _Tp > &__va) noexcept
Return an iterator pointing to one past the last element of the valarray.
Definition valarray:1249
_Tp * begin(valarray< _Tp > &__va) noexcept
Return an iterator pointing to the first element of the valarray.
Definition valarray:1227
exception_ptr current_exception() noexcept
void rethrow_exception(exception_ptr)
Throw the object pointed to by the exception_ptr.
ISO C++ entities toplevel namespace is std.
constexpr default_sentinel_t default_sentinel
A default sentinel value.
Uniform interface to all allocator types.
An opaque pointer to an arbitrary exception.