On Sat, 18 Nov 2023 at 19:50, Arsen Arsenović <ar...@aarsen.me> wrote: > > libstdc++-v3/ChangeLog: > > * include/Makefile.am: Install std/generator, bits/elements_of.h > as freestanding. > * include/Makefile.in: Regenerate. > * include/bits/version.def: Add __cpp_lib_generator. > * include/bits/version.h: Regenerate. > * include/precompiled/stdc++.h: Include <generator>. > * include/std/ranges: Include bits/elements_of.h > * include/bits/elements_of.h: New file. > * include/std/generator: New file. > * testsuite/24_iterators/range_generators/01.cc: New test. > * testsuite/24_iterators/range_generators/02.cc: New test. > * testsuite/24_iterators/range_generators/copy.cc: New test. > * testsuite/24_iterators/range_generators/except.cc: New test. > * testsuite/24_iterators/range_generators/synopsis.cc: New test. > * testsuite/24_iterators/range_generators/subrange.cc: New test. > --- > Evening, > > This is an implementation of <generator> from C++23. It should be > feature-complete, though it doesn't have all the tests that it ought to > and is missing a few tweaks. > > Posting to get reviews in the meanwhile, in case something obvious was > missed. > > Have a lovely night :-) > > libstdc++-v3/include/Makefile.am | 2 + > libstdc++-v3/include/Makefile.in | 2 + > libstdc++-v3/include/bits/elements_of.h | 72 ++ > libstdc++-v3/include/bits/version.def | 9 + > libstdc++-v3/include/bits/version.h | 11 + > libstdc++-v3/include/precompiled/stdc++.h | 1 + > libstdc++-v3/include/std/generator | 820 ++++++++++++++++++ > libstdc++-v3/include/std/ranges | 4 + > .../24_iterators/range_generators/01.cc | 55 ++ > .../24_iterators/range_generators/02.cc | 219 +++++ > .../24_iterators/range_generators/copy.cc | 97 +++ > .../24_iterators/range_generators/except.cc | 97 +++ > .../24_iterators/range_generators/subrange.cc | 45 + > .../24_iterators/range_generators/synopsis.cc | 38 + > 14 files changed, 1472 insertions(+) > create mode 100644 libstdc++-v3/include/bits/elements_of.h > create mode 100644 libstdc++-v3/include/std/generator > create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/01.cc > create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/02.cc > create mode 100644 > libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc > create mode 100644 > libstdc++-v3/testsuite/24_iterators/range_generators/except.cc > create mode 100644 > libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc > create mode 100644 > libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc > > diff --git a/libstdc++-v3/include/Makefile.am > b/libstdc++-v3/include/Makefile.am > index 17d9d9cec313..0b764f2b8a9e 100644 > --- a/libstdc++-v3/include/Makefile.am > +++ b/libstdc++-v3/include/Makefile.am > @@ -35,6 +35,7 @@ std_freestanding = \ > ${std_srcdir}/coroutine \ > ${std_srcdir}/expected \ > ${std_srcdir}/functional \ > + ${std_srcdir}/generator \ > ${std_srcdir}/iterator \ > ${std_srcdir}/limits \ > ${std_srcdir}/memory \ > @@ -122,6 +123,7 @@ bits_freestanding = \ > ${bits_srcdir}/concept_check.h \ > ${bits_srcdir}/char_traits.h \ > ${bits_srcdir}/cpp_type_traits.h \ > + ${bits_srcdir}/elements_of.h \ > ${bits_srcdir}/enable_special_members.h \ > ${bits_srcdir}/functexcept.h \ > ${bits_srcdir}/functional_hash.h \ > diff --git a/libstdc++-v3/include/Makefile.in > b/libstdc++-v3/include/Makefile.in > index f038af709cc4..7f1a6592942e 100644 > --- a/libstdc++-v3/include/Makefile.in > +++ b/libstdc++-v3/include/Makefile.in > @@ -393,6 +393,7 @@ std_freestanding = \ > ${std_srcdir}/coroutine \ > ${std_srcdir}/expected \ > ${std_srcdir}/functional \ > + ${std_srcdir}/generator \ > ${std_srcdir}/iterator \ > ${std_srcdir}/limits \ > ${std_srcdir}/memory \ > @@ -477,6 +478,7 @@ bits_freestanding = \ > ${bits_srcdir}/concept_check.h \ > ${bits_srcdir}/char_traits.h \ > ${bits_srcdir}/cpp_type_traits.h \ > + ${bits_srcdir}/elements_of.h \ > ${bits_srcdir}/enable_special_members.h \ > ${bits_srcdir}/functexcept.h \ > ${bits_srcdir}/functional_hash.h \ > diff --git a/libstdc++-v3/include/bits/elements_of.h > b/libstdc++-v3/include/bits/elements_of.h > new file mode 100644 > index 000000000000..663e15a94aa7 > --- /dev/null > +++ b/libstdc++-v3/include/bits/elements_of.h > @@ -0,0 +1,72 @@ > +// Tag type for yielding ranges rather than values in <generator> -*- C++ > -*- > + > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +#ifndef _GLIBCXX_BITS_ELEMENTS_OF > +#define _GLIBCXX_BITS_ELEMENTS_OF > + > +#pragma GCC system_header > + > +#include <bits/c++config.h> > + > +#include <bits/version.h> > + > +// C++ >= 23 && __glibcxx_coroutine > +#if defined(__glibcxx_ranges) && defined(__glibcxx_generator) > +#include <bits/ranges_base.h> > +#include <bits/memoryfwd.h> > + > +#if _GLIBCXX_HOSTED > +# include <bits/allocator.h> // likely desirable if hosted. > +#endif // HOSTED > + > +namespace std _GLIBCXX_VISIBILITY(default) > +{ > +_GLIBCXX_BEGIN_NAMESPACE_VERSION > +namespace ranges > +{ > + > + /** > + * @ingroup ranges > + * @since C++23 > + * @{ > + */ > + > + template<range _Range, typename _Alloc = allocator<byte>> > + struct elements_of > + { > + [[no_unique_address]] _Range range; > + [[no_unique_address]] _Alloc allocator = _Alloc(); > + }; > + > + template<typename _Range, typename _Alloc = allocator<byte>> > + elements_of(_Range&&, _Alloc = _Alloc()) > + -> elements_of<_Range&&, _Alloc>; > + > + /// @} > +} > +_GLIBCXX_END_NAMESPACE_VERSION > +} // namespace std > + > +#endif // __glibcxx_generator && __glibcxx_ranges > +#endif // _GLIBCXX_BITS_ELEMENTS_OF > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 447fdeb95193..1edee0dbd27b 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1649,6 +1649,15 @@ ftms = { > }; > }; > > +ftms = { > + name = generator; > + values = { > + v = 202207; > + cxxmin = 23; > + extra_cond = "__glibcxx_coroutine"; > + }; > +}; > + > // Standard test specifications. > stds[97] = ">= 199711L"; > stds[03] = ">= 199711L"; > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index 97c6d8508f48..b323ea9dab9e 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2021,4 +2021,15 @@ > #endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) > */ > #undef __glibcxx_want_to_string > > +// from version.def line 1637 > +#if !defined(__cpp_lib_generator) > +# if (__cplusplus >= 202302L) && (__glibcxx_coroutine) > +# define __glibcxx_generator 202207L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_generator) > +# define __cpp_lib_generator 202207L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) > */ > +#undef __glibcxx_want_generator > + > #undef __glibcxx_want_all > diff --git a/libstdc++-v3/include/precompiled/stdc++.h > b/libstdc++-v3/include/precompiled/stdc++.h > index 176ad79ff3c3..baad95dd6c3e 100644 > --- a/libstdc++-v3/include/precompiled/stdc++.h > +++ b/libstdc++-v3/include/precompiled/stdc++.h > @@ -222,6 +222,7 @@ > > #if __cplusplus > 202002L > #include <expected> > +#include <generator> > #include <spanstream> > #if __has_include(<stacktrace>) > # include <stacktrace> > diff --git a/libstdc++-v3/include/std/generator > b/libstdc++-v3/include/std/generator > new file mode 100644 > index 000000000000..a06811d3dd96 > --- /dev/null > +++ b/libstdc++-v3/include/std/generator > @@ -0,0 +1,820 @@ > +// <generator> -*- C++ -*- > + > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +/** @file include/generator > + * This is a Standard C++ Library header. > + */ > + > +#ifndef _GLIBCXX_GENERATOR > +#define _GLIBCXX_GENERATOR > + > +#include <ranges> > +#pragma GCC system_header > + > +#include <bits/c++config.h> > + > +#define __glibcxx_want_generator > +#include <bits/version.h> > + > +#if __cplusplus < 202302L > +# error "std::generator is a C++23 extension" > +#endif
Please remove this. Giving an error when a header is included breaks this pattern: #if __has_include(<generator>) #include <generator> #ifdef __cpp_lib_generator // ... use it ... #endif We have such errors for C++11 headers, but those predate the existence of __has_include and feature test macros anyway. For new headers from C++14 and later we don't do an #error. The header should just be empty. > + > +#ifdef __cpp_lib_generator // C++ >= 23 && __glibcxx_coroutine > +#include <new> > +#include <bits/move.h> > +#include <bits/ranges_util.h> > +#include <bits/elements_of.h> > +#include <bits/uses_allocator.h> > +#include <bits/exception_ptr.h> > +#include <cstddef> > +#include <cstdint> > +#include <cstring> > +#include <coroutine> > + > +#include <type_traits> > +#include <variant> > +#include <concepts> > + > +#if _GLIBCXX_HOSTED > +# include <bits/memory_resource.h> > +#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<typename _Ref, typename _V = void, typename _Alloc = void> > + class generator; > + > + /// @cond undocumented > + namespace __gen > + { > + /// _Reference type for a generator whose reference (first argument) and > + /// value (second argument) types are _Ref and _V. > + template<typename _Ref, typename _V> We avoid single letter names, because they clash with libc macros like _T and _X. I don't think _V is actually used by any libc, but for consistency please avoid it. So maybe _Val instead. > + using _Reference_t = __conditional_t<is_void_v<_V>, > + _Ref&&, _Ref>; > + > + /// Type yielded by a generator whose _Reference type is _Reference. > + template<typename _Reference> > + using _Yield_t = __conditional_t<is_reference_v<_Reference>, > + _Reference, > + const _Reference&>; > + > + /// _Yield_t * _Reference_t > + template<typename _Ref, typename _V> > + using _Yield2_t = _Yield_t<_Reference_t<_Ref, _V>>; > + > + template<typename> struct _Is_generator_t : std::false_type {}; > + template<typename _V, typename _R, typename _A> > + struct _Is_generator_t<::std::generator<_V, _R, _A>> : std::true_type > {}; > + > + template<typename _T> > + concept _Is_generator = _Is_generator_t<remove_cvref_t<_T>>::value; We usually name concepts all lowercase, so __is_generator. But it looks like we don't need a concept here anyway, it's only tested in a static_assert, which can use any boolean constant. We don't need to use _Is_generator in requires-clauses or anything like that. Unless the intention is to allow users to specialize the _Is_generator_t class template for their own types (is it?) then that class template and the concept can be replaced with a variable template, which will compile more efficiently: template<typename> constexpr bool __is_generator = false; template<typename _Val, typename _Ref, typename _Alloc> constexpr bool __is_generator<std::generator<_Val, _Ref, _Alloc>> = true; Then this can be used directly in the static_assert. Alternatively, the __is_specialization_of variable template in <format> could be moved to another header, and then you could just use __is_specialization_of<std::generator>. > + > + /// Allocator and value type erased generator promise type. > + /// \tparam _Yielded The corresponding generators yielded type. > + template<typename _Yielded> > + 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<typename, typename, typename> > + friend struct std::generator; s/struct/class/ > + > + template<typename _Gen> > + struct _Recursive_awaiter; > + template<typename> > + 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(__val, _M_bottom_value()); } > + > + template<typename _R2, typename _V2, typename _A2, typename _U2> > + requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded> > + auto > + yield_value(ranges::elements_of<generator<_R2, _V2, _A2>&&, _U2> __r) > + noexcept > + { return _Recursive_awaiter { std::move(__r.range) }; } > + > + template<ranges::input_range _R, typename _Alloc> > + requires convertible_to<ranges::range_reference_t<_R>, _Yielded> > + auto > + yield_value(ranges::elements_of<_R, _Alloc> __r) > + noexcept > + { > + 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<typename _Yielded> > + 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 of 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<typename _Yielded> > + struct _Promise_erased<_Yielded>::_Final_awaiter > + { > + bool await_ready() noexcept > + { return false; } > + > + template<typename _Promise> > + auto await_suspend(std::coroutine_handle<_Promise> __c) noexcept > + { > + static_assert(is_pointer_interconvertible_base_of_v< > + _Promise_erased, _Promise>); > + > + auto& __n = __c.promise()._M_nest; > + return __n._M_pop(); > + } > + > + void await_resume() noexcept {} > + }; > + > + template<typename _Yielded> > + struct _Promise_erased<_Yielded>::_Copy_awaiter > + { > + _Yielded_decvref _M_value; > + _ValuePtr& _M_bottom_value; > + > + constexpr bool await_ready() noexcept > + { return false; } > + > + template<typename _Promise> > + void await_suspend(std::coroutine_handle<_Promise>) noexcept > + { > + static_assert(is_pointer_interconvertible_base_of_v< > + _Promise_erased, _Promise>); > + _M_bottom_value = ::std::addressof(_M_value); > + } > + > + constexpr void > + await_resume() const noexcept > + {} > + }; > + > + template<typename _Yielded> > + template<typename _Gen> > + struct _Promise_erased<_Yielded>::_Recursive_awaiter > + { > + _Gen _M_gen; > + static_assert(_Is_generator<_Gen>); > + static_assert(std::same_as<typename _Gen::yielded, _Yielded>); > + > + _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<typename _Promise> > + std::coroutine_handle<> > + await_suspend(std::coroutine_handle<_Promise> __p) noexcept > + { > + static_assert(is_pointer_interconvertible_base_of_v< > + _Promise_erased, _Promise>); > + > + 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<typename _A> > + concept _Stateless_alloc = (allocator_traits<_A>::is_always_equal::value > + && default_initializable<_A>); > + > + template<typename _Alloc> > + class _Promise_alloc > + { > + using _ATr = allocator_traits<_Alloc>; > + using _Rebound = typename _ATr::template rebind_alloc<_Alloc_block>; > + using _Rebound_ATr = typename _ATr > + ::template rebind_traits<_Alloc_block>; > + static_assert(is_pointer_v<typename _Rebound_ATr::pointer>, > + "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<std::uintptr_t>(__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> // _Alloc is non-void > + { return _M_allocate({}, __sz); } > + > + template<typename _NA, typename... _Args> > + void* > + operator new(std::size_t __sz, > + allocator_arg_t, const _NA& __na, > + const _Args&...) > + requires convertible_to<const _NA&, _Alloc> > + { > + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), > + __sz); > + } > + > + template<typename _This, typename _NA, typename... _Args> > + void* > + operator new(std::size_t __sz, > + const _This&, > + allocator_arg_t, const _NA& __na, > + const _Args&...) > + requires convertible_to<const _NA&, _Alloc> > + { > + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), > + __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<std::uintptr_t>(__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<void> > + { > + 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<typename _Rebound> > + 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<std::uintptr_t>(__da); > + __aan += sizeof(_Dealloc_fn); > + auto __aligned = ((__aan + __ba - 1) / __ba) * __ba; > + return reinterpret_cast<_Rebound*>(__aligned); > + } > + > + template<typename _Rebound> > + 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<typename _Rebound> > + 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<std::uintptr_t>(__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<typename _NA> > + static void* > + _M_allocate(const _NA& __na, std::size_t __csz) > + { > + using _Rebound = typename std::allocator_traits<_NA> > + ::template rebind_alloc<_Alloc_block>; > + using _Rebound_ATr = typename std::allocator_traits<_NA> > + ::template rebind_traits<_Alloc_block>; > + > + static_assert(is_pointer_v<typename _Rebound_ATr::pointer>, > + "Must use allocators for true pointers with > generators"); > + > + _Dealloc_fn __d = &_M_deallocator<_Rebound>; > + auto __b = static_cast<_Rebound>(__na); > + auto __asz = _M_alloc_size<_Rebound>(__csz); > + auto __nblk = _Alloc_block::_M_cnt(__asz); > + void* __p = __b.allocate(__nblk); > + auto __pn = reinterpret_cast<std::uintptr_t>(__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<void>(__sz); > + _Dealloc_fn __d = [] (void* __ptr, std::size_t __sz) > + { > + ::operator delete(__ptr, _M_alloc_size<void>(__sz)); > + }; > + auto __p = ::operator new(__nsz); > + auto __pn = reinterpret_cast<uintptr_t>(__p); > + *_M_dealloc_address(__pn, __sz) = __d; > + return __p; > + } > + > + template<typename _NA, typename... _Args> Please use _Na instead of _NA so it's not all-caps. > + void* > + operator new(std::size_t __sz, > + allocator_arg_t, const _NA& __na, > + const _Args&...) > + { return _M_allocate(__na, __sz); } > + > + template<typename _This, typename _NA, typename... _Args> > + void* > + operator new(std::size_t __sz, > + const _This&, > + allocator_arg_t, const _NA& __na, > + const _Args&...) > + { return _M_allocate(__na, __sz); } > + > + void > + operator delete(void* __ptr, std::size_t __sz) noexcept > + { > + _Dealloc_fn __d; > + auto __pn = reinterpret_cast<uintptr_t>(__ptr); > + __d = *_M_dealloc_address(__pn, __sz); > + __d(__ptr, __sz); > + } > + }; > + > + template<typename _T> s/T/_Tp/ > + concept _Cv_unqualified_object = is_object_v<_T> > + && same_as<_T, remove_cv_t<_T>>; > + } // namespace __gen > + /// @endcond > + > + template<typename _Ref, typename _V, typename _Alloc> > + class generator > + : public ranges::view_interface<generator<_Ref, _V, _Alloc>> > + { > + using _Value = __conditional_t<is_void_v<_V>, remove_cvref_t<_Ref>, > _V>; > + static_assert(__gen::_Cv_unqualified_object<_Value>, > + "Generator value must be a cv-unqualified object type"); > + using _Reference = __gen::_Reference_t<_Ref, _V>; > + 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<promise_type>::from_promise(*this) }; } > + }; > + > + static_assert(is_pointer_interconvertible_base_of_v<_Erased_promise, > + promise_type>); > + > + 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); > + } > + > + _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<promise_type> __coro) noexcept > + : _M_coro { move(__coro) } > + {} > + > + void > + _M_mark_as_started() noexcept > + { > + __glibcxx_assert(!this->_M_began); > + this->_M_began = true; > + } > + > + coroutine_handle<promise_type> _M_coro; > + bool _M_began = false; > + }; > + > + template<class _Ref, class _V, class _Alloc> > + struct generator<_Ref, _V, _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(); } __i > + > + 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++(); } > + > + yielded > + operator*() > + const noexcept(is_nothrow_move_constructible_v<_Reference>) > + { > + auto& __p = this->_M_coro.promise(); > + return static_cast<yielded>(*__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<class R, class V = void> s/class/typename/ And uglify the names (_Ref and _Val). > + using generator = std::generator<R, V, polymorphic_allocator<std::byte>>; > + } > +#endif // HOSTED > + > +_GLIBCXX_END_NAMESPACE_VERSION > +} // namespace std > +#endif // __cpp_lib_generator > + > +#endif // _GLIBCXX_GENERATOR > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index 26d6c013ad04..fde8c6cd706d 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -67,6 +67,10 @@ > #define __glibcxx_want_ranges_zip > #include <bits/version.h> > > +#ifdef __glibcxx_generator // C++ >= 23 && __glibcxx_coroutine > +# include <bits/elements_of.h> > +#endif > + > /** > * @defgroup ranges Ranges > * > diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc > b/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc > new file mode 100644 > index 000000000000..bedbec3d353f > --- /dev/null > +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc > @@ -0,0 +1,55 @@ > +// { dg-do run { target c++23 } } > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +#include <generator> > +#include <iostream> > + > +// basic example > +std::generator<int> > +bar() > +{ > + co_yield 3; > + co_yield 4; > +} > + > +std::generator<int> > +foo() > +{ > + co_yield 1; > + co_yield 2; > + co_yield std::ranges::elements_of { bar() }; > + co_yield 5; > +} > + > +int > +main() > +{ > + for (auto x : foo()) > + std::cout << x << '\n'; > +} > + > +// { dg-output {1(\n|\r\n|\r)} } > +// { dg-output {2(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {4(\n|\r\n|\r)} } > +// { dg-output {5(\n|\r\n|\r)} } > diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc > b/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc > new file mode 100644 > index 000000000000..570daedca75b > --- /dev/null > +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc > @@ -0,0 +1,219 @@ > +// { dg-do run { target c++23 } } > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +#include <iostream> > +#include <generator> > +#include <testsuite_hooks.h> > + > +struct foo > +{ > + int id; > + > + foo(int id) > + : id { id } > + {} > + > + foo(const foo& o) > + : id { o.id * 100 } > + { > + std::cout << "copy-consed " << o.id << "->" << id << '\n'; > + } > + > + foo& > + operator=(const foo& o) > + { > + id = o.id * 100; > + std::cout << "copied " << o.id << "->" << id << '\n'; > + return *this; > + } > + > + foo(foo&& o) > + : id { o.id } > + { > + o.id = -1; > + std::cout << "moved " << id << '\n'; > + } > + > + foo& > + operator=(foo&& o) > + { > + std::swap(o.id, id); > + std::cout << "swapped " << id << '\n'; > + return *this; > + } > +}; > + > +std::generator<foo> > +foogen() > +{ > + co_yield foo{0}; > + > + { > + foo f {1}; > + co_yield f; > + } > + > + { > + const foo f {2}; > + co_yield f; > + } > + > + { > + foo f {3}; > + co_yield std::move(f); > + } > +} > + > +std::generator<const foo&> > +foogen2() > +{ > + co_yield foo{0}; > + > + { > + foo f {1}; > + co_yield f; > + } > + > + { > + const foo f {2}; > + co_yield f; > + } > + > + { > + foo f {3}; > + co_yield std::move(f); > + } > +} > + > +std::generator<foo&&> > +foogen3() > +{ > + co_yield foo{0}; > + > + { > + foo f {1}; > + co_yield f; > + } > + > + { > + const foo f {2}; > + co_yield f; > + } > + > + { > + foo f {3}; > + co_yield std::move(f); > + } > +} > + > +int > +main() > +{ > + for (auto f : foogen()) > + std::cout << f.id << '\n'; > + for (const auto& f : foogen()) > + std::cout << f.id << '\n'; > + for (auto&& f : foogen()) > + std::cout << f.id << '\n'; > + > + std::cout << "---\n"; > + > + for (auto f : foogen2()) > + std::cout << f.id << '\n'; > + for (const auto& f : foogen2()) > + std::cout << f.id << '\n'; > + for (auto&& f : foogen2()) > + std::cout << f.id << '\n'; > + > + std::cout << "---\n"; > + > + for (auto f : foogen3()) > + std::cout << f.id << '\n'; > + for (const auto& f : foogen3()) > + std::cout << f.id << '\n'; > + for (auto&& f : foogen3()) > + std::cout << f.id << '\n'; > +} > + > +// { dg-output {moved 0(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } > +// { dg-output {moved 100(\n|\r\n|\r)} } > +// { dg-output {100(\n|\r\n|\r)} } > +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } > +// { dg-output {moved 200(\n|\r\n|\r)} } > +// { dg-output {200(\n|\r\n|\r)} } > +// { dg-output {moved 3(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } > +// { dg-output {100(\n|\r\n|\r)} } > +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } > +// { dg-output {200(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } > +// { dg-output {100(\n|\r\n|\r)} } > +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } > +// { dg-output {200(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {---(\n|\r\n|\r)} } > +// { dg-output {copy-consed 0->0(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } > +// { dg-output {100(\n|\r\n|\r)} } > +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } > +// { dg-output {200(\n|\r\n|\r)} } > +// { dg-output {copy-consed 3->300(\n|\r\n|\r)} } > +// { dg-output {300(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {1(\n|\r\n|\r)} } > +// { dg-output {2(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {1(\n|\r\n|\r)} } > +// { dg-output {2(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {---(\n|\r\n|\r)} } > +// { dg-output {moved 0(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } > +// { dg-output {moved 100(\n|\r\n|\r)} } > +// { dg-output {100(\n|\r\n|\r)} } > +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } > +// { dg-output {moved 200(\n|\r\n|\r)} } > +// { dg-output {200(\n|\r\n|\r)} } > +// { dg-output {moved 3(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } > +// { dg-output {100(\n|\r\n|\r)} } > +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } > +// { dg-output {200(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > +// { dg-output {0(\n|\r\n|\r)} } > +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } > +// { dg-output {100(\n|\r\n|\r)} } > +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } > +// { dg-output {200(\n|\r\n|\r)} } > +// { dg-output {3(\n|\r\n|\r)} } > diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc > b/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc > new file mode 100644 > index 000000000000..5e5474d0de53 > --- /dev/null > +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc > @@ -0,0 +1,97 @@ > +// { dg-do run { target c++23 } } > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +#include <testsuite_hooks.h> > +#include <generator> > + > +template<unsigned MaxCopies> > +struct copy_max > +{ > + int copy = 0; > + > + copy_max() > + {} > + > + copy_max(const copy_max& o) > + : copy {o.copy + 1} > + { > + VERIFY(copy <= MaxCopies); > + } > + > + copy_max& > + operator=(const copy_max& o) > + { > + copy = o.copy + 1; > + VERIFY(copy <= MaxCopies); > + return *this; > + } > + > + copy_max(copy_max&& o) > + { > + std::swap(o.copy, this->copy); > + } > + > + copy_max& > + operator=(copy_max&& o) > + { > + std::swap(o.copy, this->copy); > + return *this; > + } > +}; > + > +template<typename Ref> > +std::generator<Ref> > +foo() > +{ > + co_yield {}; > +} > + > +int > +main() > +{ > + static_assert(!std::copy_constructible<std::generator<int>>); > + { > + auto gen = foo<const copy_max<1>&>(); > + auto i = gen.begin(); > + *i; > + *i; > + auto is = *i; > + VERIFY(is.copy > 0); > + } > + > + { > + auto gen2 = foo<copy_max<0>&&>(); > + auto i = gen2.begin(); > + *i; > + *i; > + auto is = *i; > + } > + > + { > + auto gen = foo<copy_max<0>>(); // should be same as case 2 > + auto i = gen.begin(); > + *i; > + *i; > + auto is = *i; > + } > +} > diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/except.cc > b/libstdc++-v3/testsuite/24_iterators/range_generators/except.cc > new file mode 100644 > index 000000000000..76f59b0aaed7 > --- /dev/null > +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/except.cc > @@ -0,0 +1,97 @@ > +// { dg-do run { target c++23 } } > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +#include <testsuite_hooks.h> > +#include <generator> > + > +std::generator<int> > +foo() > +{ > + co_yield 0; > + throw 3; /* dice roll */ > +} > + > +std::generator<int> > +foo_delegator() > +{ > + co_yield 1; > + co_yield std::ranges::elements_of { foo() }; > +} > + > +bool catchy_caught = false; > + > +std::generator<int> > +foo_catchy_delegator() > +{ > + try > + { > + co_yield std::ranges::elements_of { foo() }; > + VERIFY(false); > + } > + catch (int i) > + { > + catchy_caught = true; > + VERIFY(i == 3); > + } > +} > + > +int > +main() > +{ > + { > + auto gen = foo(); > + try > + { > + auto it = gen.begin(); > + VERIFY(*it == 0); > + it++; > + VERIFY(false); > + } > + catch (int x) > + { > + VERIFY(x == 3); > + } > + } > + > + { > + auto gen = foo_delegator(); > + auto it = gen.begin(); > + VERIFY(*it == 1); > + it++; > + > + try > + { > + VERIFY(*it == 0); > + it++; > + VERIFY(false); > + } > + catch (int x) > + { > + VERIFY(x == 3); > + } > + } > + > + for (auto x : foo_catchy_delegator()) > + VERIFY(x == 0); > + VERIFY(catchy_caught); > +} > diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc > b/libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc > new file mode 100644 > index 000000000000..4bd0b2f9e078 > --- /dev/null > +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc > @@ -0,0 +1,45 @@ > +// { dg-do run { target c++23 } } > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +#include <generator> > +#include <ranges> > +#include <vector> > +#include <iostream> > + > +std::generator<int&> > +yield_vector() > +{ > + std::vector foo { 1, 2, 3 }; > + auto x = 123; > + co_yield x; > + co_yield std::ranges::elements_of { foo }; > + x = 456; > + co_yield x; > +} > + > +int > +main() > +{ > + for (auto x : yield_vector()) > + std::cout << x << '\n'; > +} > diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc > b/libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc > new file mode 100644 > index 000000000000..6c037f2f3c05 > --- /dev/null > +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc > @@ -0,0 +1,38 @@ > +// { dg-do compile { target c++23 } } > +// { dg-add-options no_pch } > +// Copyright (C) 2023 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 > +// <http://www.gnu.org/licenses/>. > + > +#include <generator> > + > +#if !defined(__cpp_lib_generator) || __cpp_lib_generator < 202207L > +# error "__cpp_lib_generator undefined or has wrong value" > +#endif > + > +namespace test { > + using std::generator; > +#if __STDC_HOSTED__ > + namespace pmr { > + using std::pmr::generator; > + } > +#endif > +} > -- > 2.42.1 >