Hi, This is v2 of my generators patch. It addresses Jonathans review comments, but does not add more tests yet :-/
Original series: https://inbox.sourceware.org/20231118195008.579211-1-ar...@aarsen.me/ Changes since v1: - Uglify some symbols - Convert _Is_generator concept to __is_generator CE bool - Add "libstdc++: add missing include in ranges_util.h" - this can be pushed separately, really, but I forgot to send it. Range-diff: 1: feab374887e5 = 1212: c4286af0c70f libstdc++: add missing include in ranges_util.h 2: 010eab271755 ! 1213: fb589641656f libstdc++: implement std::generator @@ libstdc++-v3/include/std/generator (new) +#define __glibcxx_want_generator +#include <bits/version.h> + -+#if __cplusplus < 202302L -+# error "std::generator is a C++23 extension" -+#endif -+ +#ifdef __cpp_lib_generator // C++ >= 23 && __glibcxx_coroutine +#include <new> +#include <bits/move.h> @@ libstdc++-v3/include/std/generator (new) + { + /// _Reference type for a generator whose reference (first argument) and + /// value (second argument) types are _Ref and _V. -+ template<typename _Ref, typename _V> -+ using _Reference_t = __conditional_t<is_void_v<_V>, ++ template<typename _Ref, typename _Val> ++ using _Reference_t = __conditional_t<is_void_v<_Val>, + _Ref&&, _Ref>; + + /// Type yielded by a generator whose _Reference type is _Reference. @@ libstdc++-v3/include/std/generator (new) + 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; ++ template<typename _Ref, typename _Val> ++ using _Yield2_t = _Yield_t<_Reference_t<_Ref, _Val>>; + ++ template<typename> constexpr bool __is_generator = false; ++ template<typename _Val, typename _Ref, typename _Alloc> ++ constexpr bool __is_generator<std::generator<_Val, _Ref, _Alloc>> = true; + + /// Allocator and value type erased generator promise type. + /// \tparam _Yielded The corresponding generators yielded type. @@ libstdc++-v3/include/std/generator (new) + using _Coro_handle = std::coroutine_handle<_Promise_erased>; + + template<typename, typename, typename> -+ friend struct std::generator; ++ friend class std::generator; + + template<typename _Gen> + struct _Recursive_awaiter; @@ libstdc++-v3/include/std/generator (new) + __rn._M_top() = __new; + + // Presume we're the second frame... -+ auto& __bott = __rest; ++ 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 @@ libstdc++-v3/include/std/generator (new) + struct _Promise_erased<_Yielded>::_Recursive_awaiter + { + _Gen _M_gen; -+ static_assert(_Is_generator<_Gen>); ++ static_assert(__is_generator<_Gen>); + static_assert(std::same_as<typename _Gen::yielded, _Yielded>); + + _Recursive_awaiter(_Gen __gen) noexcept @@ libstdc++-v3/include/std/generator (new) + requires default_initializable<_Rebound> // _Alloc is non-void + { return _M_allocate({}, __sz); } + -+ template<typename _NA, typename... _Args> ++ template<typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) -+ requires convertible_to<const _NA&, _Alloc> ++ requires convertible_to<const _Na&, _Alloc> + { + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), + __sz); + } + -+ template<typename _This, typename _NA, typename... _Args> ++ template<typename _This, typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, + const _This&, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) -+ requires convertible_to<const _NA&, _Alloc> ++ requires convertible_to<const _Na&, _Alloc> + { + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), + __sz); @@ libstdc++-v3/include/std/generator (new) + } + } + -+ template<typename _NA> ++ template<typename _Na> + static void* -+ _M_allocate(const _NA& __na, std::size_t __csz) ++ _M_allocate(const _Na& __na, std::size_t __csz) + { -+ using _Rebound = typename std::allocator_traits<_NA> ++ using _Rebound = typename std::allocator_traits<_Na> + ::template rebind_alloc<_Alloc_block>; -+ using _Rebound_ATr = typename std::allocator_traits<_NA> ++ using _Rebound_ATr = typename std::allocator_traits<_Na> + ::template rebind_traits<_Alloc_block>; + + static_assert(is_pointer_v<typename _Rebound_ATr::pointer>, @@ libstdc++-v3/include/std/generator (new) + return __p; + } + -+ template<typename _NA, typename... _Args> ++ template<typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) + { return _M_allocate(__na, __sz); } + -+ template<typename _This, typename _NA, typename... _Args> ++ template<typename _This, typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, + const _This&, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) + { return _M_allocate(__na, __sz); } + @@ libstdc++-v3/include/std/generator (new) + } + }; + -+ template<typename _T> -+ concept _Cv_unqualified_object = is_object_v<_T> -+ && same_as<_T, remove_cv_t<_T>>; ++ template<typename _Tp> ++ concept _Cv_unqualified_object = is_object_v<_Tp> ++ && same_as<_Tp, remove_cv_t<_Tp>>; + } // namespace __gen + /// @endcond + @@ libstdc++-v3/include/std/generator (new) + using difference_type = ptrdiff_t; + + friend bool -+ operator==(const _Iterator& i, default_sentinel_t) noexcept -+ { return i._M_coro.done(); } ++ operator==(const _Iterator& __i, default_sentinel_t) noexcept ++ { return __i._M_coro.done(); } + + friend class generator; + @@ libstdc++-v3/include/std/generator (new) + +#if _GLIBCXX_HOSTED + namespace pmr { -+ template<class R, class V = void> -+ using generator = std::generator<R, V, polymorphic_allocator<std::byte>>; ++ template<typename _Ref, typename _Val = void> ++ using generator = std::generator<_Ref, _Val, polymorphic_allocator<std::byte>>; + } +#endif // HOSTED + Tested libstdc++ on x86_64-pc-linux-gnu. TIA, have a lovely day. Arsen Arsenović (2): libstdc++: add missing include in ranges_util.h libstdc++: implement std::generator libstdc++-v3/include/Makefile.am | 2 + libstdc++-v3/include/Makefile.in | 2 + libstdc++-v3/include/bits/elements_of.h | 72 ++ libstdc++-v3/include/bits/ranges_util.h | 1 + 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 | 812 ++++++++++++++++++ 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 + 15 files changed, 1465 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 -- 2.43.0