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 60 namespace 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 326 static_assert(is_pointer_interconvertible_base_of_v< 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 350 static_assert(is_pointer_interconvertible_base_of_v< 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 383 static_assert(is_pointer_interconvertible_base_of_v< 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) 396 std::rethrow_exception(__e); 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 822