STL/stl/inc/generator

607 строки
26 KiB
C++

// generator standard header
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef _GENERATOR_
#define _GENERATOR_
#include <yvals_core.h>
#if _STL_COMPILER_PREPROCESSOR
#if !_HAS_CXX23
_EMIT_STL_WARNING(STL4038, "The contents of <generator> are available only with C++23 or later.");
#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv
#include <coroutine>
#include <exception>
#include <xmemory>
#ifdef __cpp_lib_byte
#include <xpolymorphic_allocator.h>
#endif // defined(__cpp_lib_byte)
#pragma pack(push, _CRT_PACKING)
#pragma warning(push, _STL_WARNING_LEVEL)
#pragma warning(disable : _STL_DISABLED_WARNINGS)
_STL_DISABLE_CLANG_WARNINGS
#pragma push_macro("new")
#undef new
// TRANSITION, non-_Ugly attribute tokens
#pragma push_macro("empty_bases")
#undef empty_bases
_STD_BEGIN
struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) _Aligned_block {
unsigned char _Pad[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
};
template <class _Alloc>
constexpr bool _Stateless_allocator = default_initializable<_Alloc> && allocator_traits<_Alloc>::is_always_equal::value;
_EXPORT_STD template <class _Rty, class _Vty = void, class _Alloc = void>
class generator;
namespace _Gen_detail {
template <class _Alloc>
concept _Valid_allocator = same_as<_Alloc, void> || is_pointer_v<typename allocator_traits<_Alloc>::pointer>;
template <class _Alloc>
_NODISCARD _Alloc* _Get_allocator_address(void* const _Ptr, const size_t _Size) noexcept {
_STL_INTERNAL_STATIC_ASSERT(!_Stateless_allocator<_Alloc>);
const auto _Al_address =
(reinterpret_cast<uintptr_t>(_Ptr) + _Size + alignof(_Alloc) - 1) & ~(alignof(_Alloc) - 1);
return reinterpret_cast<_Alloc*>(_Al_address);
}
template <class _Proto_allocator>
class _Promise_allocator { // statically specified allocator type
private:
using _Alloc = _Rebind_alloc_t<_Proto_allocator, _Aligned_block>;
using _Alloc_size_type = allocator_traits<_Alloc>::size_type;
_NODISCARD static size_t _Allocation_block_size(const size_t _Size) noexcept {
// Compute the number of _Aligned_blocks needed to store a coroutine
// frame of _Size bytes and, if necessary, an _Alloc.
if constexpr (_Stateless_allocator<_Alloc>) {
// allocator is stateless, we need not store it
return (_Size + sizeof(_Aligned_block) - 1) / sizeof(_Aligned_block);
} else {
// allocator is stateful, we need storage for it
constexpr size_t _Align = (_STD max)(alignof(_Alloc), sizeof(_Aligned_block));
return (_Size + sizeof(_Alloc) + _Align - 1) / sizeof(_Aligned_block);
}
}
_NODISCARD_RAW_PTR_ALLOC static void* _Allocate(_Alloc _Al, const size_t _Size) {
// memory layout:
// |--- coroutine frame --|- alignment padding -|---- _Alloc -----|
// |--- always present ---|----------- stateful only -------------|
// all rounded up to an integral number of _Aligned_blocks
const size_t _Size_in_blocks = _Allocation_block_size(_Size);
void* const _Ptr = _Al.allocate(_Convert_size<_Alloc_size_type>(_Size_in_blocks));
if constexpr (!_Stateless_allocator<_Alloc>) {
// store only stateful allocators
const auto _Al_address = _Get_allocator_address<_Alloc>(_Ptr, _Size);
_STD construct_at(_Al_address, _STD move(_Al));
}
return _Ptr;
}
_NODISCARD static _Alloc _Get_allocator(
[[maybe_unused]] void* const _Ptr, [[maybe_unused]] const size_t _Size) noexcept {
// recreate or retrieve a copy of the allocator used to allocate _Ptr
if constexpr (_Stateless_allocator<_Alloc>) {
return _Alloc{};
} else {
const auto _Al_address = _Get_allocator_address<_Alloc>(_Ptr, _Size);
return *_Al_address;
}
}
public:
_NODISCARD_RAW_PTR_ALLOC static void* operator new(const size_t _Size)
requires default_initializable<_Alloc>
{
return _Allocate(_Alloc{}, _Size);
}
template <class _Alloc2, class... _Args>
requires convertible_to<const _Alloc2&, _Proto_allocator>
_NODISCARD_RAW_PTR_ALLOC static void* operator new(
const size_t _Size, allocator_arg_t, const _Alloc2& _Al, const _Args&...) {
return _Allocate(static_cast<_Alloc>(static_cast<_Proto_allocator>(_Al)), _Size);
}
template <class _This, class _Alloc2, class... _Args>
requires convertible_to<const _Alloc2&, _Proto_allocator>
_NODISCARD_RAW_PTR_ALLOC static void* operator new(
const size_t _Size, const _This&, allocator_arg_t, const _Alloc2& _Al, const _Args&...) {
return _Allocate(static_cast<_Alloc>(static_cast<_Proto_allocator>(_Al)), _Size);
}
static void operator delete(void* const _Ptr, const size_t _Size) noexcept {
_Alloc _Al = _Get_allocator(_Ptr, _Size);
const size_t _Size_in_blocks = _Allocation_block_size(_Size);
_Al.deallocate(static_cast<_Aligned_block*>(_Ptr), static_cast<_Alloc_size_type>(_Size_in_blocks));
}
};
template <>
class _Promise_allocator<void> { // type-erased allocator
private:
using _Dealloc_fn = void(__stdcall*)(void*, size_t) _NOEXCEPT_FNPTR;
template <class _Alloc>
_NODISCARD static size_t _Allocation_block_size(const size_t _Size) noexcept {
size_t _Bytes = _Size + sizeof(_Dealloc_fn);
if constexpr (_Stateless_allocator<_Alloc>) {
_Bytes += sizeof(_Aligned_block) - 1;
} else {
constexpr size_t _Align = (_STD max)(alignof(_Alloc), sizeof(_Aligned_block));
_Bytes += sizeof(_Alloc) + _Align - 1;
}
return _Bytes / sizeof(_Aligned_block);
}
template <class _Alloc>
static void __stdcall _Dealloc_stateless(void* const _Ptr, const size_t _Size) noexcept {
_STL_INTERNAL_STATIC_ASSERT(_Stateless_allocator<_Alloc>);
_Alloc _Al{};
const size_t _Size_in_blocks = _Allocation_block_size<_Alloc>(_Size);
const auto _Alloc_size = static_cast<allocator_traits<_Alloc>::size_type>(_Size_in_blocks);
_Al.deallocate(static_cast<_Aligned_block*>(_Ptr), _Alloc_size);
}
template <class _Alloc>
static void __stdcall _Dealloc_stateful(void* const _Ptr, const size_t _Size) noexcept {
_STL_INTERNAL_STATIC_ASSERT(!_Stateless_allocator<_Alloc>);
const auto _Al_address = _Get_allocator_address<_Alloc>(_Ptr, _Size + sizeof(_Dealloc_fn));
auto& _Stored_al = *_Al_address;
_Alloc _Al{_STD move(_Stored_al)};
_Stored_al.~_Alloc();
const size_t _Size_in_blocks = _Allocation_block_size<_Alloc>(_Size);
const auto _Alloc_size = static_cast<allocator_traits<_Alloc>::size_type>(_Size_in_blocks);
_Al.deallocate(static_cast<_Aligned_block*>(_Ptr), _Alloc_size);
}
static void __stdcall _Dealloc_default(void* const _Ptr, const size_t _Size) noexcept {
::operator delete(_Ptr, _Size + sizeof(_Dealloc_fn));
}
template <class _ProtoAlloc>
_NODISCARD_RAW_PTR_ALLOC static void* _Allocate(const _ProtoAlloc& _Proto, const size_t _Size) {
// memory layout:
// |--- coroutine frame (_Size bytes) ---|--- _Dealloc_fn ---|-- alignment padding --|--- _Alloc ---|
// |-------------------- always present -----(not aligned)---|------------ stateful only -----------|
// all rounded up to an integral number of _Aligned_blocks
using _Alloc = _Rebind_alloc_t<_ProtoAlloc, _Aligned_block>;
using _Alloc_size_type = allocator_traits<_Alloc>::size_type;
_Alloc _Al{_Proto};
const size_t _Size_in_blocks = _Allocation_block_size<_Alloc>(_Size);
const auto _Alloc_size = _Convert_size<_Alloc_size_type>(_Size_in_blocks);
void* const _Ptr = _Al.allocate(_Alloc_size);
if constexpr (_Stateless_allocator<_Alloc>) {
// don't store stateless allocator
const _Dealloc_fn _Dealloc = _Dealloc_stateless<_Alloc>;
_CSTD memcpy(static_cast<char*>(_Ptr) + _Size, &_Dealloc, sizeof(_Dealloc_fn));
return _Ptr;
} else {
// store stateful allocator
const _Dealloc_fn _Dealloc = _Dealloc_stateful<_Alloc>;
_CSTD memcpy(static_cast<char*>(_Ptr) + _Size, &_Dealloc, sizeof(_Dealloc_fn));
const auto _Al_address = _Get_allocator_address<_Alloc>(_Ptr, _Size + sizeof(_Dealloc_fn));
_STD construct_at(_Al_address, _STD move(_Al));
return _Ptr;
}
}
public:
_NODISCARD_RAW_PTR_ALLOC static void* operator new(const size_t _Size) {
void* const _Ptr = ::operator new(_Size + sizeof(_Dealloc_fn));
const _Dealloc_fn _Dealloc = _Dealloc_default;
_CSTD memcpy(static_cast<char*>(_Ptr) + _Size, &_Dealloc, sizeof(_Dealloc_fn));
return _Ptr;
}
template <class _Alloc, class... _Args>
_NODISCARD_RAW_PTR_ALLOC static void* operator new(
const size_t _Size, allocator_arg_t, const _Alloc& _Al, const _Args&...) {
static_assert(_Valid_allocator<_Alloc>, "generator allocators must use raw pointers "
"(N4988 [coro.generator.class]/1.1)");
return _Allocate(_Al, _Size);
}
template <class _This, class _Alloc, class... _Args>
_NODISCARD_RAW_PTR_ALLOC static void* operator new(
const size_t _Size, const _This&, allocator_arg_t, const _Alloc& _Al, const _Args&...) {
static_assert(_Valid_allocator<_Alloc>, "generator allocators must use raw pointers "
"(N4988 [coro.generator.class]/1.1)");
return _Allocate(_Al, _Size);
}
static void operator delete(void* const _Ptr, const size_t _Size) noexcept {
_Dealloc_fn _Dealloc;
_CSTD memcpy(&_Dealloc, static_cast<const char*>(_Ptr) + _Size, sizeof(_Dealloc_fn));
_Dealloc(_Ptr, _Size);
}
};
template <class _Rty, class _Vty>
using _Value_t = conditional_t<is_void_v<_Vty>, remove_cvref_t<_Rty>, _Vty>;
template <class _Rty, class _Vty>
using _Reference_t = conditional_t<is_void_v<_Vty>, _Rty&&, _Rty>;
template <class _Ref>
using _Yield_t = conditional_t<is_reference_v<_Ref>, _Ref, const _Ref&>;
template <class, class>
struct _Iter_provider {
class _Iterator;
};
template <class _Yielded>
class _Promise_base {
public:
_STL_INTERNAL_STATIC_ASSERT(is_reference_v<_Yielded>);
#ifndef _PREFAST_ // TRANSITION, VSO-1662733
_NODISCARD
#endif // ^^^ no workaround ^^^
suspend_always initial_suspend() const noexcept {
return {};
}
_NODISCARD auto final_suspend() noexcept {
return _Final_awaiter{};
}
_NODISCARD suspend_always yield_value(_Yielded _Val) noexcept {
_Ptr = _STD addressof(_Val);
return {};
}
_NODISCARD auto yield_value(const remove_reference_t<_Yielded>& _Val)
noexcept(is_nothrow_constructible_v<remove_cvref_t<_Yielded>,
const remove_reference_t<_Yielded>&>) /* strengthened */
requires is_rvalue_reference_v<_Yielded>
&& constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
{
return _Element_awaiter{_Val};
}
template <class _Rty, class _Vty, class _Alloc, class _Unused>
requires same_as<_Yield_t<_Reference_t<_Rty, _Vty>>, _Yielded>
_NODISCARD auto yield_value(_RANGES elements_of<generator<_Rty, _Vty, _Alloc>&&, _Unused> _Elem) noexcept {
using _Nested_awaitable = _Nested_awaitable_provider<_Rty, _Vty, _Alloc>::_Awaitable;
return _Nested_awaitable{_STD move(_Elem.range)};
}
template <_RANGES input_range _Rng, class _Alloc>
requires convertible_to<_RANGES range_reference_t<_Rng>, _Yielded>
_NODISCARD auto yield_value(_RANGES elements_of<_Rng, _Alloc> _Elem) {
using _Vty = _RANGES range_value_t<_Rng>;
using _Nested_awaitable = _Nested_awaitable_provider<_Yielded, _Vty, _Alloc>::_Awaitable;
auto _Lambda = [](allocator_arg_t, _Alloc, _RANGES iterator_t<_Rng> _It,
const _RANGES sentinel_t<_Rng> _Se) -> generator<_Yielded, _Vty, _Alloc> {
for (; _It != _Se; ++_It) {
co_yield static_cast<_Yielded>(*_It);
}
};
return _Nested_awaitable{
_Lambda(allocator_arg, _Elem.allocator, _RANGES begin(_Elem.range), _RANGES end(_Elem.range))};
}
void await_transform() = delete;
void return_void() const noexcept {}
void unhandled_exception() {
if (const auto _Info = _Try_get_nest_info()) {
_Info->_Except = _STD current_exception();
} else {
_RERAISE;
}
}
private:
struct _Element_awaiter {
remove_cvref_t<_Yielded> _Val;
explicit _Element_awaiter(const remove_reference_t<_Yielded>& _Val_)
noexcept(is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
: _Val(_Val_) {}
_NODISCARD constexpr bool await_ready() const noexcept {
return false;
}
template <class _CoroPromise>
constexpr void await_suspend(coroutine_handle<_CoroPromise> _Handle) noexcept {
#ifdef __cpp_lib_is_pointer_interconvertible // TRANSITION, LLVM-48860
_STL_INTERNAL_STATIC_ASSERT(is_pointer_interconvertible_base_of_v<_Promise_base, _CoroPromise>);
#endif // ^^^ no workaround ^^^
_Promise_base& _Current = _Handle.promise();
_Current._Ptr = _STD addressof(_Val);
}
constexpr void await_resume() const noexcept {}
};
// generator's stack of nested coroutines is realized as a linked list of promises. The "root" promise is the
// original that existed before any nested yields. The "top" promise is the one currently being iterated over.
// Iterators hold handles for the root's coroutine and the root promise holds a link to the top promise so
// iterators can resume it to generate the next element when incremented. Each promise keeps a link to the root
// so they can update the top link as promises are pushed and popped. It looks a bit like:
//
// +--------------------------- Top ---------------------------+
// v |
// +=====+ +=====+ +=====+ +=====+
// | |-- Parent -->| |-- Parent -->| |-- Parent -->| |
// +=====+ +=====+ +=====+ +=====+
// | | | ^
// +------- Root ------+------- Root ------+------- Root ------+
//
// The parent and root links are actually stored out-of-line in a _Nest_info. Since a promise is either a root
// or is nested, the single promise member _Data can be used to store either a top link or a pointer to a
// _Nest_info.
struct _Nest_info {
exception_ptr _Except;
coroutine_handle<_Promise_base> _Parent;
coroutine_handle<_Promise_base> _Root;
};
struct _Final_awaiter {
_NODISCARD bool await_ready() const noexcept {
return false;
}
template <class _CoroPromise>
_NODISCARD coroutine_handle<> await_suspend(coroutine_handle<_CoroPromise> _Handle) const noexcept {
// Resume _Handle's parent coroutine, if any.
#ifdef __cpp_lib_is_pointer_interconvertible // TRANSITION, LLVM-48860
_STL_INTERNAL_STATIC_ASSERT(is_pointer_interconvertible_base_of_v<_Promise_base, _CoroPromise>);
#endif // ^^^ no workaround ^^^
if (const auto _Info = _Handle.promise()._Try_get_nest_info()) {
coroutine_handle<_Promise_base> _Cont = _Info->_Parent;
_Info->_Root.promise()._Set_top(_Cont);
return _Cont;
}
return _STD noop_coroutine();
}
void await_resume() const noexcept {}
};
template <class _Rty, class _Vty, class _Alloc>
struct _Nested_awaitable_provider {
struct _Awaitable {
_STL_INTERNAL_STATIC_ASSERT(same_as<_Yield_t<_Reference_t<_Rty, _Vty>>, _Yielded>);
_Nest_info _Nested;
generator<_Rty, _Vty, _Alloc> _Gen;
explicit _Awaitable(generator<_Rty, _Vty, _Alloc>&& _Gen_) noexcept : _Gen(_STD move(_Gen_)) {}
_NODISCARD bool await_ready() const noexcept {
return !_Gen._Coro;
}
template <class _CoroPromise>
_NODISCARD coroutine_handle<_Promise_base> await_suspend(
coroutine_handle<_CoroPromise> _Current) noexcept {
// Push _Gen's coroutine onto the coroutine stack that _Current is at the top of.
#ifdef __cpp_lib_is_pointer_interconvertible // TRANSITION, LLVM-48860
_STL_INTERNAL_STATIC_ASSERT(is_pointer_interconvertible_base_of_v<_Promise_base, _CoroPromise>);
#endif // ^^^ no workaround ^^^
auto _Target = coroutine_handle<_Promise_base>::from_address(_Gen._Coro.address());
_Nested._Parent = coroutine_handle<_Promise_base>::from_address(_Current.address());
if (const auto _Parent_nest_info = _Nested._Parent.promise()._Try_get_nest_info()) {
_Nested._Root = _Parent_nest_info->_Root;
} else {
_Nested._Root = _Nested._Parent;
}
_Nested._Root.promise()._Set_top(_Target);
_Target.promise()._Set_nest_info(_STD addressof(_Nested));
return _Target;
}
void await_resume() {
if (_Nested._Except) {
_STD rethrow_exception(_STD move(_Nested._Except));
}
}
};
};
_NODISCARD _Nest_info* _Try_get_nest_info() const noexcept {
if ((_Data & 1U) != 0) {
return reinterpret_cast<_Nest_info*>(_Data ^ 1U);
}
return nullptr;
}
_NODISCARD coroutine_handle<_Promise_base> _Get_top() const noexcept {
_STL_INTERNAL_CHECK((_Data & 1U) == 0);
return coroutine_handle<_Promise_base>::from_address(reinterpret_cast<void*>(_Data));
}
void _Set_nest_info(_Nest_info* _Info) noexcept {
_Data = reinterpret_cast<uintptr_t>(_Info) | 1U;
}
void _Set_top(coroutine_handle<_Promise_base> _Top) noexcept {
_Data = reinterpret_cast<uintptr_t>(_Top.address());
}
template <class, class>
friend struct _Iter_provider;
// Least significant bit of `_Data` indicates stored information:
// LSB 0: `_Data` is a top coroutine handle,
// LSB 1: `_Data ^ 1U` is a pointer to an object of type `_Nest_info`.
uintptr_t _Data = reinterpret_cast<uintptr_t>(coroutine_handle<_Promise_base>::from_promise(*this).address());
add_pointer_t<_Yielded> _Ptr = nullptr;
};
struct _Secret_tag {
explicit _Secret_tag() = default;
};
template <class _Value, class _Ref>
class _Iter_provider<_Value, _Ref>::_Iterator {
public:
using value_type = _Value;
using difference_type = ptrdiff_t;
_Iterator(_Iterator&& _That) noexcept : _Coro{_STD exchange(_That._Coro, {})} {}
_Iterator& operator=(_Iterator&& _That) noexcept {
_Coro = _STD exchange(_That._Coro, {});
return *this;
}
_NODISCARD _Ref operator*() const
noexcept(noexcept(static_cast<_Ref>(*_Coro.promise()._Get_top().promise()._Ptr))) /* strengthened */ {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Coro.done(), "Can't dereference generator end iterator");
#endif // _CONTAINER_DEBUG_LEVEL > 0
return static_cast<_Ref>(*_Coro.promise()._Get_top().promise()._Ptr);
}
_Iterator& operator++() {
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(!_Coro.done(), "Can't increment generator end iterator");
#endif // _CONTAINER_DEBUG_LEVEL > 0
_Coro.promise()._Get_top().resume();
return *this;
}
void operator++(int) {
++*this;
}
_NODISCARD friend bool operator==(const _Iterator& _It, default_sentinel_t) noexcept /* strengthened */ {
return _It._Coro.done();
}
private:
template <class, class, class>
friend class _STD generator;
explicit _Iterator(_Secret_tag, coroutine_handle<_Promise_base<_Yield_t<_Ref>>> _Coro_) noexcept
: _Coro{_Coro_} {}
coroutine_handle<_Promise_base<_Yield_t<_Ref>>> _Coro;
};
} // namespace _Gen_detail
_EXPORT_STD template <class _Rty, class _Vty, class _Alloc>
class generator : public _RANGES view_interface<generator<_Rty, _Vty, _Alloc>> {
private:
static_assert(_Gen_detail::_Valid_allocator<_Alloc>,
"generator allocators must use raw pointers (N4988 [coro.generator.class]/1.1)");
using _Value = _Gen_detail::_Value_t<_Rty, _Vty>;
static_assert(same_as<remove_cvref_t<_Value>, _Value> && is_object_v<_Value>,
"generator's value type must be a cv-unqualified object type (N4988 [coro.generator.class]/1.2)");
using _Ref = _Gen_detail::_Reference_t<_Rty, _Vty>;
static_assert(
is_reference_v<_Ref> || (is_object_v<_Ref> && same_as<remove_cv_t<_Ref>, _Ref> && copy_constructible<_Ref>),
"generator's selected reference type must be an actual reference type or a cv-unqualified copy-constructible "
"object type (N4988 [coro.generator.class]/1.3)");
using _RRef = conditional_t<is_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
static_assert(common_reference_with<_Ref&&, _Value&> && common_reference_with<_Ref&&, _RRef&&>
&& common_reference_with<_RRef&&, const _Value&>,
"generator's iterator type must model indirectly_readable, but that's impossible with the selected value and "
"reference types (N4988 [coro.generator.class]/1.4)");
public:
using yielded = _Gen_detail::_Yield_t<_Ref>;
friend _Gen_detail::_Promise_base<yielded>;
struct __declspec(empty_bases) promise_type : _Gen_detail::_Promise_allocator<_Alloc>,
_Gen_detail::_Promise_base<yielded> {
_NODISCARD generator get_return_object() noexcept {
return generator{_Gen_detail::_Secret_tag{}, coroutine_handle<promise_type>::from_promise(*this)};
}
};
_STL_INTERNAL_STATIC_ASSERT(is_standard_layout_v<promise_type>);
#ifdef __cpp_lib_is_pointer_interconvertible // TRANSITION, LLVM-48860
_STL_INTERNAL_STATIC_ASSERT(
is_pointer_interconvertible_base_of_v<_Gen_detail::_Promise_base<yielded>, promise_type>);
#endif // ^^^ no workaround ^^^
generator(generator&& _That) noexcept : _Coro(_STD exchange(_That._Coro, {})) {}
~generator() {
if (_Coro) {
_Coro.destroy();
}
}
generator& operator=(generator _That) noexcept {
_STD swap(_Coro, _That._Coro);
return *this;
}
_NODISCARD _Gen_detail::_Iter_provider<_Value, _Ref>::_Iterator begin() {
// Pre: _Coro is suspended at its initial suspend point
#if _CONTAINER_DEBUG_LEVEL > 0
_STL_VERIFY(_Coro, "Can't call begin on moved-from generator");
#endif // _CONTAINER_DEBUG_LEVEL > 0
_Coro.resume();
return typename _Gen_detail::_Iter_provider<_Value, _Ref>::_Iterator{_Gen_detail::_Secret_tag{},
coroutine_handle<_Gen_detail::_Promise_base<yielded>>::from_address(_Coro.address())};
}
_NODISCARD default_sentinel_t end() const noexcept {
return default_sentinel;
}
private:
coroutine_handle<promise_type> _Coro = nullptr;
// The stack of coroutine handles depicted in the Standard is realized by a linked structure of promises and
// awaitable objects, so all necessary storage is allocated in coroutine frames. See the description in the body of
// _Gen_detail::_Promise_base.
explicit generator(_Gen_detail::_Secret_tag, coroutine_handle<promise_type> _Coro_) noexcept : _Coro(_Coro_) {}
};
#ifdef __cpp_lib_byte
namespace pmr {
_EXPORT_STD template <class _Rty, class _Vty = void>
using generator = _STD generator<_Rty, _Vty, polymorphic_allocator<>>;
} // namespace pmr
#endif // defined(__cpp_lib_byte)
_STD_END
// TRANSITION, non-_Ugly attribute tokens
#pragma pop_macro("empty_bases")
#pragma pop_macro("new")
_STL_RESTORE_CLANG_WARNINGS
#pragma warning(pop)
#pragma pack(pop)
#endif // ^^^ _HAS_CXX23 ^^^
#endif // _STL_COMPILER_PREPROCESSOR
#endif // _GENERATOR_