зеркало из https://github.com/microsoft/STL.git
607 строки
26 KiB
C++
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_
|