[libcxx-commits] [libcxx] [libc++] P2502R2: `std::generator` (PR #92213)
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Jul 17 10:59:14 PDT 2024
================
@@ -0,0 +1,593 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_GENERATOR
+#define _LIBCPP_GENERATOR
+
+/*
+
+namespace std {
+ // [coro.generator.class], class template generator
+ template<class Ref, class V = void, class Allocator = void>
+ class generator;
+
+ namespace pmr {
+ template<class R, class V = void>
+ using generator = std::generator<R, V, polymorphic_allocator<>>;
+ }
+}
+
+*/
+
+#include <__algorithm/max.h>
+#include <__assert>
+#include <__concepts/common_reference_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/same_as.h>
+#include <__config>
+#include <__coroutine/coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/noop_coroutine_handle.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <__exception/exception_ptr.h>
+#include <__iterator/default_sentinel.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/elements_of.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
+#include <__utility/exchange.h>
+#include <__utility/move.h>
+#include <__utility/swap.h>
+#include <cstdint>
+#include <new>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Ref, class _Val>
+using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+
+template <class _Ref, class _Val>
+using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+
+template <class _Ref, class _Val>
+using __gen_yielded =
+ conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
+
+template <class, class, class>
+class generator;
+
+template <class _Yielded>
+class __gen_promise_base {
+private:
+ template <class, class, class>
+ friend class generator;
+
+ template <class, class>
+ friend class __gen_iter;
+
+ // Each promise object stores either a `__root_data` when associated with a root generator, or a `__recursive_data`
+ // when associated with a generator that is yielded recursively.
+ struct __root_data {
+ // The client code has access only to the iterator of the root generator. Thus, the root generator must store the
+ // yielded values of recursively-yielded generators, which will then be returned when the client code dereferences
+ // the iterator.
+ add_pointer_t<_Yielded> __value_ptr;
+ // The client code has access only to the iterator of the root generator. Thus, the root generator needs to identify
+ // which generator is currently active. This active generator will then be resumed when the client code increments
+ // the iterator.
+ std::coroutine_handle<__gen_promise_base> __active;
+ };
+
+ struct __recursive_data {
+ std::exception_ptr __exception;
+ std::coroutine_handle<__gen_promise_base> __parent;
+ std::coroutine_handle<__gen_promise_base> __root;
+ };
+
+ union __union {
+ __root_data __root;
+ __recursive_data __recursive;
+
+ _LIBCPP_HIDE_FROM_ABI ~__union() noexcept {}
+ } __data_;
+
+ // The `__tag_` stores the active member of the `__data_` union:
+ // - `false` indicates that the active member is `__root`.
+ // - `true` indicates that the active member is `__recursive`.
+ // This field can be omitted because `__recursive_data.__root` can store the active member.
+ bool __tag_;
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_root() noexcept { return !__tag_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __root_data& __get_root_data() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+ return __data_.__root;
+ }
+ _LIBCPP_HIDE_FROM_ABI void __set_root_tag() noexcept { __tag_ = false; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_recursive() noexcept { return __tag_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __recursive_data& __get_recursive_data() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_recursive(), "the active member of `__data_` is not `__recursive`");
+ return __data_.__recursive;
+ }
+ _LIBCPP_HIDE_FROM_ABI void __set_recursive_tag() noexcept { __tag_ = true; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::coroutine_handle<__gen_promise_base>& __active() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+ return __get_root_data().__active;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __value_ptr() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+ return __get_root_data().__value_ptr;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __root_value_ptr() noexcept {
+ if (__is_root()) {
+ return __value_ptr();
+ }
+ return __get_recursive_data().__root.promise().__value_ptr();
+ }
+
+ struct __element_awaiter {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+ template <class _Promise>
+ _LIBCPP_HIDE_FROM_ABI void await_suspend(coroutine_handle<_Promise> __current) noexcept {
+ __current.promise().__root_value_ptr() = std::addressof(__value);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+
+ remove_cvref_t<_Yielded> __value;
+ };
+
+ struct __final_awaiter {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+ template <class _Promise>
+ _LIBCPP_HIDE_FROM_ABI coroutine_handle<> await_suspend(coroutine_handle<_Promise> __current) noexcept {
+ // Checks if the current generator is recursively-yielded
+ if (__current.promise().__is_recursive()) {
+ auto&& __recursive_data = __current.promise().__get_recursive_data();
+ auto __parent = __recursive_data.__parent;
+ // Updates the active generator to its parent, allowing the client code to resume it later
+ __recursive_data.__root.promise().__active() = __parent;
+ // Transfers execution to its parent, which is the generator that yields it
+ return __parent;
+ }
+ return std::noop_coroutine();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+ };
+
+ template <class _Ref, class _Val, class _Allocator>
+ struct __recursive_awaiter {
+ _LIBCPP_HIDE_FROM_ABI explicit __recursive_awaiter(generator<_Ref, _Val, _Allocator>&& __gen) noexcept
+ : __gen_{std::move(__gen)} {}
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen_.__coroutine_; }
+
+ template <class _Promise>
+ _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>
+ await_suspend(coroutine_handle<_Promise> __current) noexcept {
+ // Stores a `__recursive_data` in the promise object associated with `__gen`
+ auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+ __recursive.promise().__set_recursive_tag();
+ auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+
+ // Sets `__recursive_data.__parent` to the current generator
+ auto __parent = coroutine_handle<__gen_promise_base>::from_address(__current.address());
+ __recursive_data.__parent = __parent;
+
+ // Sets `__recursive_data.__root` to the current generator if it's a root generator, or to the root generator of
+ // the current generator otherwise
+ if (__parent.promise().__is_recursive()) {
+ __recursive_data.__root = __parent.promise().__get_recursive_data().__root;
+ } else {
+ __recursive_data.__root = __parent;
+ }
+
+ // Updates the active generator to `__gen`, allowing the client code to resume it later
+ __recursive_data.__root.promise().__active() = __recursive;
+
+ // Transfers execution to `__gen`
+ return __recursive;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_resume() {
+ auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen_.__coroutine_.address());
+ auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+ if (__recursive_data.__exception) {
+ std::rethrow_exception(std::move(__recursive_data.__exception));
+ }
+ }
+
+ private:
+ generator<_Ref, _Val, _Allocator> __gen_;
+ };
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
+ : __data_{.__root{
+ .__value_ptr{nullptr},
+ .__active{coroutine_handle<__gen_promise_base>::from_promise(*this)},
+ }},
+ __tag_{false} {}
+
+ _LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
+ if (__is_root()) {
+ __data_.__root.~__root_data();
+ } else {
+ __data_.__recursive.~__recursive_data();
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI suspend_always initial_suspend() const noexcept { return {}; }
+
+ _LIBCPP_HIDE_FROM_ABI __final_awaiter final_suspend() noexcept { return {}; }
+
+ _LIBCPP_HIDE_FROM_ABI suspend_always yield_value(_Yielded __value) noexcept {
+ __root_value_ptr() = std::addressof(__value);
+ return {};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value) noexcept(
+ is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
+ requires is_rvalue_reference_v<_Yielded> &&
+ constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
+ {
+ return __element_awaiter{.__value{__value}};
+ }
+
+ template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+ requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+ _LIBCPP_HIDE_FROM_ABI auto
+ yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&&, _Unused> __elements) noexcept {
+ return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+ }
+
+ template <ranges::input_range _Range, class _Allocator>
+ requires convertible_to<ranges::range_reference_t<_Range>, _Yielded>
+ _LIBCPP_HIDE_FROM_ABI auto yield_value(ranges::elements_of<_Range, _Allocator> __range) {
+ auto __lambda =
+ [](allocator_arg_t, _Allocator, ranges::iterator_t<_Range> __i, ranges::sentinel_t<_Range> __s) static
+ -> generator<_Yielded, ranges::range_value_t<_Range>, _Allocator> {
+ for (; __i != __s; ++__i) {
+ co_yield static_cast<_Yielded>(*__i);
+ }
+ };
+ return yield_value(ranges::elements_of(
+ __lambda(allocator_arg, __range.allocator, ranges::begin(__range.range), ranges::end(__range.range))));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_transform() = delete;
+
+ _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
+
+ _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ if (__is_root()) {
+ throw;
+ } else {
+ __get_recursive_data().__exception = std::current_exception();
+ }
+# endif
+ }
+};
+
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __gen_aligned_block {
+ char __data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+ _LIBCPP_HIDE_FROM_ABI static size_t __count(const size_t __size) noexcept {
+ return (__size + sizeof(__gen_aligned_block) - 1) / sizeof(__gen_aligned_block);
+ };
+};
+
+template <class _Allocator>
+concept __gen_stateless_allocator =
+ default_initializable<_Allocator> && allocator_traits<_Allocator>::is_always_equal::value;
+
+template <class _Allocator>
+class __gen_promise_allocator {
+private:
+ using __rebind = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+ using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+ using __rebind_pointer = allocator_traits<__rebind>::pointer;
+ static_assert(is_pointer_v<__rebind_pointer>);
+
+ _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+ __rebind __rebind_al = static_cast<__rebind>(__al);
+
+ if constexpr (__gen_stateless_allocator<__rebind>) {
+ const size_t __block_count = __gen_aligned_block::__count(__size);
+ return __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+ } else {
+ // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
+ // stateful allocator
+ const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+ void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+ // Stores the stateful allocator in the allocated block
+ __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+ (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+ std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+ return __ptr;
+ }
+ }
+
+public:
+ _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+ requires default_initializable<_Allocator>
+ {
+ return __alloc({}, __size);
+ }
+
+ template <class _Allocator2, class... _Args>
+ requires convertible_to<const _Allocator2&, _Allocator>
+ _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+ return __alloc(static_cast<_Allocator>(__al), __size);
+ }
+
+ template <class _This, class _Allocator2, class... _Args>
+ requires convertible_to<const _Allocator2&, _Allocator>
+ _LIBCPP_HIDE_FROM_ABI void*
+ operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+ return __alloc(static_cast<_Allocator>(__al), __size);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+ if constexpr (__gen_stateless_allocator<__rebind>) {
+ __rebind __rebind_al;
+ const size_t __block_count = __gen_aligned_block::__count(__size);
+ __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+ } else {
+ // Retrieves the stateful allocator stored in the allocated block
+ __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+ (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+ __rebind __rebind_al = std::move(*__rebind_al_ptr);
+ std::destroy_at(__rebind_al_ptr);
+
+ const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+ __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+ }
+ }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+ using __dealloc_fn = void (*)(void*, size_t) noexcept;
+
+ template <class _Allocator>
+ _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+ using __rebind = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+ using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+ using __rebind_pointer = allocator_traits<__rebind>::pointer;
+ static_assert(is_pointer_v<__rebind_pointer>);
+
+ __rebind __rebind_al = static_cast<__rebind>(__al);
+ if constexpr (__gen_stateless_allocator<__rebind>) {
+ const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+ void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+ // Stores the deallocation function in the allocated block
+ __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+ *__dealloc_ptr = __dealloc<__rebind>;
+ return __ptr;
+ } else {
+ // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
+ // stateful allocator, and the deallocation function
+ const size_t __block_count =
+ __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(__rebind) + alignof(__rebind));
+ void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+ // Stores the deallocation function in the allocated block
+ __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+ *__dealloc_ptr = __dealloc<__rebind>;
+
+ // Stores the stateful allocator in the allocated block
+ __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+ (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
+ ~(alignof(__rebind) - 1));
+ std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+ return __ptr;
+ }
+ }
+
+ template <class _Rebind>
+ _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, size_t __size) noexcept {
+ using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+ if constexpr (__gen_stateless_allocator<_Rebind>) {
+ _Rebind __rebind_al;
+ const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+ __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+ } else {
+ // Retrieves the stateful allocator stored in the allocated block
+ _Rebind* const __rebind_al_ptr = reinterpret_cast<_Rebind*>(
+ (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
+ ~(alignof(_Rebind) - 1));
+ _Rebind __rebind_al = std::move(*__rebind_al_ptr);
+ std::destroy_at(__rebind_al_ptr);
+
+ const size_t __block_count =
+ __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
+ __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+ }
+ }
+
+public:
+ _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
+ void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
+ __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+ ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+ };
+
+ __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+ *__dealloc_ptr = __dealloc;
+ return __ptr;
+ }
+
+ template <class _Allocator, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+ return __alloc(__al, __size);
+ }
+
+ template <class _This, class _Allocator, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI void*
+ operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+ return __alloc(__al, __size);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
+ __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+ const __dealloc_fn __dealloc = *__dealloc_ptr;
+ __dealloc(__ptr, __size);
+ }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter {
+private:
+ using __val = __gen_val<_Ref, _Val>;
+ using __ref = __gen_ref<_Ref, _Val>;
+
+public:
+ using value_type = __val;
+ using difference_type = ptrdiff_t;
+
+ _LIBCPP_HIDE_FROM_ABI explicit __gen_iter(
+ coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+ : __coroutine_{__coroutine} {}
+
+ _LIBCPP_HIDE_FROM_ABI __gen_iter(__gen_iter&& __other) noexcept
+ : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+ _LIBCPP_HIDE_FROM_ABI __gen_iter& operator=(__gen_iter&& __other) noexcept {
+ __coroutine_ = std::exchange(__other.__coroutine_, {});
+ return *this;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __ref operator*() const noexcept(is_nothrow_copy_constructible_v<__ref>) {
+ return static_cast<__ref>(*__coroutine_.promise().__value_ptr());
+ }
+
+ _LIBCPP_HIDE_FROM_ABI __gen_iter& operator++() {
+ __coroutine_.promise().__active().resume();
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __gen_iter& __iter, default_sentinel_t) noexcept {
+ return __iter.__coroutine_.done();
+ }
+
+private:
+ coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
+};
+
+template <class _Ref, class _Val = void, class _Allocator = void>
+class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
+private:
+ using __val = __gen_val<_Ref, _Val>;
+ static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
----------------
mordante wrote:
Can you add .verify tests for these Mandates?
https://github.com/llvm/llvm-project/pull/92213
More information about the libcxx-commits
mailing list