// -*- C++ -*- // Copyright (C) 2023-2025 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . /** @file include/generator * This is a Standard C++ Library header. */ #ifndef _GLIBCXX_GENERATOR #define _GLIBCXX_GENERATOR #include #ifdef _GLIBCXX_SYSHDR #pragma GCC system_header #endif #include #define __glibcxx_want_generator #include #ifdef __cpp_lib_generator // C++ >= 23 && __glibcxx_coroutine #include #include #include #include #include #include #include #include #include #include #include #include #include #if _GLIBCXX_HOSTED # include #endif // HOSTED namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION /** * @defgroup generator_coros Range generator coroutines * @addtogroup ranges * @since C++23 * @{ */ /** @brief A range specified using a yielding coroutine. * * `std::generator` is a utility class for defining ranges using coroutines * that yield elements as a range. Generator coroutines are synchronous. * * @headerfile generator * @since C++23 */ template class generator; /// @cond undocumented namespace __gen { /// _Reference type for a generator whose reference (first argument) and /// value (second argument) types are _Ref and _Val. template using _Reference_t = __conditional_t, _Ref&&, _Ref>; /// Type yielded by a generator whose _Reference type is _Reference. template using _Yield_t = __conditional_t, _Reference, const _Reference&>; /// _Yield_t * _Reference_t template using _Yield2_t = _Yield_t<_Reference_t<_Ref, _Val>>; template constexpr bool __is_generator = false; template constexpr bool __is_generator> = true; /// Allocator and value type erased generator promise type. /// \tparam _Yielded The corresponding generators yielded type. template class _Promise_erased { static_assert(is_reference_v<_Yielded>); using _Yielded_deref = remove_reference_t<_Yielded>; using _Yielded_decvref = remove_cvref_t<_Yielded>; using _ValuePtr = add_pointer_t<_Yielded>; using _Coro_handle = std::coroutine_handle<_Promise_erased>; template friend class std::generator; template struct _Recursive_awaiter; template friend struct _Recursive_awaiter; struct _Copy_awaiter; struct _Subyield_state; struct _Final_awaiter; public: suspend_always initial_suspend() const noexcept { return {}; } suspend_always yield_value(_Yielded __val) noexcept { _M_bottom_value() = ::std::addressof(__val); return {}; } auto yield_value(const _Yielded_deref& __val) noexcept (is_nothrow_constructible_v<_Yielded_decvref, const _Yielded_deref&>) requires (is_rvalue_reference_v<_Yielded> && constructible_from<_Yielded_decvref, const _Yielded_deref&>) { return _Copy_awaiter(_Yielded_decvref(__val), _M_bottom_value()); } template requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded> auto yield_value(ranges::elements_of&&, _U2> __r) noexcept { return _Recursive_awaiter { std::move(__r.range) }; } // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3899. co_yielding elements of an lvalue generator is // unnecessarily inefficient template requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded> auto yield_value(ranges::elements_of&, _U2> __r) noexcept { return _Recursive_awaiter { std::move(__r.range) }; } template requires convertible_to, _Yielded> auto yield_value(ranges::elements_of<_R, _Alloc> __r) { auto __n = [] (allocator_arg_t, _Alloc, ranges::iterator_t<_R> __i, ranges::sentinel_t<_R> __s) -> generator<_Yielded, ranges::range_value_t<_R>, _Alloc> { for (; __i != __s; ++__i) co_yield static_cast<_Yielded>(*__i); }; return yield_value(ranges::elements_of(__n(allocator_arg, __r.allocator, ranges::begin(__r.range), ranges::end(__r.range)))); } _Final_awaiter final_suspend() noexcept { return {}; } void unhandled_exception() { // To get to this point, this coroutine must have been active. In that // case, it must be the top of the stack. The current coroutine is // the sole entry of the stack iff it is both the top and the bottom. As // it is the top implicitly in this context it will be the sole entry iff // it is the bottom. if (_M_nest._M_is_bottom()) throw; else this->_M_except = std::current_exception(); } void await_transform() = delete; void return_void() const noexcept {} private: _ValuePtr& _M_bottom_value() noexcept { return _M_nest._M_bottom_value(*this); } _ValuePtr& _M_value() noexcept { return _M_nest._M_value(*this); } _Subyield_state _M_nest; std::exception_ptr _M_except; }; template struct _Promise_erased<_Yielded>::_Subyield_state { struct _Frame { _Coro_handle _M_bottom; _Coro_handle _M_parent; }; struct _Bottom_frame { _Coro_handle _M_top; _ValuePtr _M_value = nullptr; }; std::variant< _Bottom_frame, _Frame > _M_stack; bool _M_is_bottom() const noexcept { return !std::holds_alternative<_Frame>(this->_M_stack); } _Coro_handle& _M_top() noexcept { if (auto __f = std::get_if<_Frame>(&this->_M_stack)) return __f->_M_bottom.promise()._M_nest._M_top(); auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack); __glibcxx_assert(__bf); return __bf->_M_top; } void _M_push(_Coro_handle __current, _Coro_handle __subyield) noexcept { __glibcxx_assert(&__current.promise()._M_nest == this); __glibcxx_assert(this->_M_top() == __current); __subyield.promise()._M_nest._M_jump_in(__current, __subyield); } std::coroutine_handle<> _M_pop() noexcept { if (auto __f = std::get_if<_Frame>(&this->_M_stack)) { // We aren't a bottom coroutine. Restore the parent to the top // and resume. auto __p = this->_M_top() = __f->_M_parent; return __p; } else // Otherwise, there's nothing to resume. return std::noop_coroutine(); } void _M_jump_in(_Coro_handle __rest, _Coro_handle __new) noexcept { __glibcxx_assert(&__new.promise()._M_nest == this); __glibcxx_assert(this->_M_is_bottom()); // We're bottom. We're also top if top is unset (note that this is // not true if something was added to the coro stack and then popped, // but in that case we can't possibly be yielded from, as it would // require rerunning begin()). __glibcxx_assert(!this->_M_top()); auto& __rn = __rest.promise()._M_nest; __rn._M_top() = __new; // Presume we're the second frame... auto __bott = __rest; if (auto __f = std::get_if<_Frame>(&__rn._M_stack)) // But, if we aren't, get the actual bottom. We're only the second // frame if our parent is the bottom frame, i.e. it doesn't have a // _Frame member. __bott = __f->_M_bottom; this->_M_stack = _Frame { ._M_bottom = __bott, ._M_parent = __rest }; } _ValuePtr& _M_bottom_value(_Promise_erased& __current) noexcept { __glibcxx_assert(&__current._M_nest == this); if (auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack)) return __bf->_M_value; auto __f = std::get_if<_Frame>(&this->_M_stack); __glibcxx_assert(__f); auto& __p = __f->_M_bottom.promise(); return __p._M_nest._M_value(__p); } _ValuePtr& _M_value(_Promise_erased& __current) noexcept { __glibcxx_assert(&__current._M_nest == this); auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack); __glibcxx_assert(__bf); return __bf->_M_value; } }; template struct _Promise_erased<_Yielded>::_Final_awaiter { bool await_ready() noexcept { return false; } template auto await_suspend(std::coroutine_handle<_Promise> __c) noexcept { #ifdef __glibcxx_is_pointer_interconvertible static_assert(is_pointer_interconvertible_base_of_v< _Promise_erased, _Promise>); #endif auto& __n = __c.promise()._M_nest; return __n._M_pop(); } void await_resume() noexcept {} }; template struct _Promise_erased<_Yielded>::_Copy_awaiter { _Yielded_decvref _M_value; _ValuePtr& _M_bottom_value; constexpr bool await_ready() noexcept { return false; } template void await_suspend(std::coroutine_handle<_Promise>) noexcept { #ifdef __glibcxx_is_pointer_interconvertible static_assert(is_pointer_interconvertible_base_of_v< _Promise_erased, _Promise>); #endif _M_bottom_value = ::std::addressof(_M_value); } constexpr void await_resume() const noexcept {} }; template template struct _Promise_erased<_Yielded>::_Recursive_awaiter { _Gen _M_gen; static_assert(__is_generator<_Gen>); static_assert(std::same_as); _Recursive_awaiter(_Gen __gen) noexcept : _M_gen(std::move(__gen)) { this->_M_gen._M_mark_as_started(); } constexpr bool await_ready() const noexcept { return false; } template std::coroutine_handle<> await_suspend(std::coroutine_handle<_Promise> __p) noexcept { #ifdef __glibcxx_is_pointer_interconvertible static_assert(is_pointer_interconvertible_base_of_v< _Promise_erased, _Promise>); #endif auto __c = _Coro_handle::from_address(__p.address()); auto __t = _Coro_handle::from_address(this->_M_gen._M_coro.address()); __p.promise()._M_nest._M_push(__c, __t); return __t; } void await_resume() { if (auto __e = _M_gen._M_coro.promise()._M_except) std::rethrow_exception(__e); } }; struct _Alloc_block { alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) char _M_data[__STDCPP_DEFAULT_NEW_ALIGNMENT__]; static auto _M_cnt(std::size_t __sz) noexcept { auto __blksz = sizeof(_Alloc_block); return (__sz + __blksz - 1) / __blksz; } }; template concept _Stateless_alloc = (allocator_traits<_All>::is_always_equal::value && default_initializable<_All>); template class _Promise_alloc { using _Rebound = __alloc_rebind<_Allocator, _Alloc_block>; using _Rebound_ATr = allocator_traits<_Rebound>; static_assert(is_pointer_v, "Must use allocators for true pointers with generators"); static auto _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept { auto __an = __fn + __fsz; auto __ba = alignof(_Rebound); return reinterpret_cast<_Rebound*>(((__an + __ba - 1) / __ba) * __ba); } static auto _M_alloc_size(std::size_t __csz) noexcept { auto __ba = alignof(_Rebound); // Our desired layout is placing the coroutine frame, then pad out to // align, then place the allocator. The total size of that is the // size of the coroutine frame, plus up to __ba bytes, plus the size // of the allocator. return __csz + __ba + sizeof(_Rebound); } static void* _M_allocate(_Rebound __b, std::size_t __csz) { if constexpr (_Stateless_alloc<_Rebound>) // Only need room for the coroutine. return __b.allocate(_Alloc_block::_M_cnt(__csz)); else { auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz)); auto __f = __b.allocate(__nsz); auto __fn = reinterpret_cast(__f); auto __an = _M_alloc_address(__fn, __csz); ::new (__an) _Rebound(std::move(__b)); return __f; } } public: void* operator new(std::size_t __sz) requires default_initializable<_Rebound> // _Allocator is non-void { return _M_allocate({}, __sz); } // _GLIBCXX_RESOLVE_LIB_DEFECTS // 3900. The allocator_arg_t overloads of promise_type::operator new // should not be constrained template void* operator new(std::size_t __sz, allocator_arg_t, const _Alloc& __a, const _Args&...) { static_assert(convertible_to, "the allocator argument to the coroutine must be " "convertible to the generator's allocator type"); return _M_allocate(_Rebound(_Allocator(__a)), __sz); } template void* operator new(std::size_t __sz, const _This&, allocator_arg_t, const _Alloc& __a, const _Args&...) { static_assert(convertible_to, "the allocator argument to the coroutine must be " "convertible to the generator's allocator type"); return _M_allocate(_Rebound(_Allocator(__a)), __sz); } void operator delete(void* __ptr, std::size_t __csz) noexcept { if constexpr (_Stateless_alloc<_Rebound>) { _Rebound __b; return __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), _Alloc_block::_M_cnt(__csz)); } else { auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz)); auto __fn = reinterpret_cast(__ptr); auto __an = _M_alloc_address(__fn, __csz); _Rebound __b(std::move(*__an)); __an->~_Rebound(); __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nsz); } } }; template<> class _Promise_alloc { using _Dealloc_fn = void (*)(void*, std::size_t); static auto _M_dealloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept { auto __an = __fn + __fsz; auto __ba = alignof(_Dealloc_fn); auto __aligned = ((__an + __ba - 1) / __ba) * __ba; return reinterpret_cast<_Dealloc_fn*>(__aligned); } template static auto _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept requires (!_Stateless_alloc<_Rebound>) { auto __ba = alignof(_Rebound); auto __da = _M_dealloc_address(__fn, __fsz); auto __aan = reinterpret_cast(__da); __aan += sizeof(_Dealloc_fn); auto __aligned = ((__aan + __ba - 1) / __ba) * __ba; return reinterpret_cast<_Rebound*>(__aligned); } template static auto _M_alloc_size(std::size_t __csz) noexcept { // This time, we want the coroutine frame, then the deallocator // pointer, then the allocator itself, if any. std::size_t __aa = 0; std::size_t __as = 0; if constexpr (!std::same_as<_Rebound, void>) { __aa = alignof(_Rebound); __as = sizeof(_Rebound); } auto __ba = __aa + alignof(_Dealloc_fn); return __csz + __ba + __as + sizeof(_Dealloc_fn); } template static void _M_deallocator(void* __ptr, std::size_t __csz) noexcept { auto __asz = _M_alloc_size<_Rebound>(__csz); auto __nblk = _Alloc_block::_M_cnt(__asz); if constexpr (_Stateless_alloc<_Rebound>) { _Rebound __b; __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk); } else { auto __fn = reinterpret_cast(__ptr); auto __an = _M_alloc_address<_Rebound>(__fn, __csz); _Rebound __b(std::move(*__an)); __an->~_Rebound(); __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk); } } template static void* _M_allocate(const _Alloc& __a, std::size_t __csz) { using _Rebound = __alloc_rebind<_Alloc, _Alloc_block>; using _Rebound_ATr = allocator_traits<_Rebound>; static_assert(is_pointer_v, "Must use allocators for true pointers with generators"); _Dealloc_fn __d = &_M_deallocator<_Rebound>; auto __b = static_cast<_Rebound>(__a); auto __asz = _M_alloc_size<_Rebound>(__csz); auto __nblk = _Alloc_block::_M_cnt(__asz); void* __p = __b.allocate(__nblk); auto __pn = reinterpret_cast(__p); *_M_dealloc_address(__pn, __csz) = __d; if constexpr (!_Stateless_alloc<_Rebound>) { auto __an = _M_alloc_address<_Rebound>(__pn, __csz); ::new (__an) _Rebound(std::move(__b)); } return __p; } public: void* operator new(std::size_t __sz) { auto __nsz = _M_alloc_size(__sz); _Dealloc_fn __d = [] (void* __ptr, std::size_t __sz) { ::operator delete(__ptr, _M_alloc_size(__sz)); }; auto __p = ::operator new(__nsz); auto __pn = reinterpret_cast(__p); *_M_dealloc_address(__pn, __sz) = __d; return __p; } template void* operator new(std::size_t __sz, allocator_arg_t, const _Alloc& __a, const _Args&...) { return _M_allocate(__a, __sz); } template void* operator new(std::size_t __sz, const _This&, allocator_arg_t, const _Alloc& __a, const _Args&...) { return _M_allocate(__a, __sz); } void operator delete(void* __ptr, std::size_t __sz) noexcept { _Dealloc_fn __d; auto __pn = reinterpret_cast(__ptr); __d = *_M_dealloc_address(__pn, __sz); __d(__ptr, __sz); } }; template concept _Cv_unqualified_object = is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>; } // namespace __gen /// @endcond template class generator : public ranges::view_interface> { using _Value = __conditional_t, remove_cvref_t<_Ref>, _Val>; static_assert(__gen::_Cv_unqualified_object<_Value>, "Generator value must be a cv-unqualified object type"); using _Reference = __gen::_Reference_t<_Ref, _Val>; static_assert(is_reference_v<_Reference> || (__gen::_Cv_unqualified_object<_Reference> && copy_constructible<_Reference>), "Generator reference type must be either a cv-unqualified " "object type that is trivially constructible or a " "reference type"); using _RRef = __conditional_t< is_reference_v<_Reference>, remove_reference_t<_Reference>&&, _Reference>; /* Required to model indirectly_readable, and input_iterator. */ static_assert(common_reference_with<_Reference&&, _Value&&>); static_assert(common_reference_with<_Reference&&, _RRef&&>); static_assert(common_reference_with<_RRef&&, const _Value&>); using _Yielded = __gen::_Yield_t<_Reference>; using _Erased_promise = __gen::_Promise_erased<_Yielded>; struct _Iterator; friend _Erased_promise; friend struct _Erased_promise::_Subyield_state; public: using yielded = _Yielded; struct promise_type : _Erased_promise, __gen::_Promise_alloc<_Alloc> { generator get_return_object() noexcept { return { coroutine_handle::from_promise(*this) }; } }; #ifdef __glibcxx_is_pointer_interconvertible static_assert(is_pointer_interconvertible_base_of_v<_Erased_promise, promise_type>); #endif generator(const generator&) = delete; generator(generator&& __other) noexcept : _M_coro(std::__exchange(__other._M_coro, nullptr)), _M_began(std::__exchange(__other._M_began, false)) {} ~generator() { if (auto& __c = this->_M_coro) __c.destroy(); } generator& operator=(generator __other) noexcept { swap(__other._M_coro, this->_M_coro); swap(__other._M_began, this->_M_began); return *this; } _Iterator begin() { this->_M_mark_as_started(); auto __h = _Coro_handle::from_promise(_M_coro.promise()); __h.promise()._M_nest._M_top() = __h; return { __h }; } default_sentinel_t end() const noexcept { return default_sentinel; } private: using _Coro_handle = std::coroutine_handle<_Erased_promise>; generator(coroutine_handle __coro) noexcept : _M_coro { move(__coro) } {} void _M_mark_as_started() noexcept { __glibcxx_assert(!this->_M_began); this->_M_began = true; } coroutine_handle _M_coro; bool _M_began = false; }; template struct generator<_Ref, _Val, _Alloc>::_Iterator { using value_type = _Value; using difference_type = ptrdiff_t; friend bool operator==(const _Iterator& __i, default_sentinel_t) noexcept { return __i._M_coro.done(); } friend class generator; _Iterator(_Iterator&& __o) noexcept : _M_coro(std::__exchange(__o._M_coro, {})) {} _Iterator& operator=(_Iterator&& __o) noexcept { this->_M_coro = std::__exchange(__o._M_coro, {}); return *this; } _Iterator& operator++() { _M_next(); return *this; } void operator++(int) { this->operator++(); } _Reference operator*() const noexcept(is_nothrow_move_constructible_v<_Reference>) { auto& __p = this->_M_coro.promise(); return static_cast<_Reference>(*__p._M_value()); } private: friend class generator; _Iterator(_Coro_handle __g) : _M_coro { __g } { this->_M_next(); } void _M_next() { auto& __t = this->_M_coro.promise()._M_nest._M_top(); __t.resume(); } _Coro_handle _M_coro; }; /// @} #if _GLIBCXX_HOSTED namespace pmr { template using generator = std::generator<_Ref, _Val, polymorphic_allocator>; } #endif // HOSTED _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // __cpp_lib_generator #endif // _GLIBCXX_GENERATOR