libstdc++: generator Source File

libstdc++

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