查了一下,std::coroutine库没有实现默认的generator,我在github上面找了一个三方实现的std::generator。没咋看代码。但是能用。
主要就是使用协程实现递归。后根遍历二叉树。
代码结构如下,
苹果的clang 12.0.5没有完整实现协程库。所以会标红。
实际在fedora最新镜像中是可以编译通过的。
test/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
if(APPLE)
message(STATUS "This is Apple, do nothing.")
set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_PREFIX_PATH /Users/aabjfzhu/software/vcpkg/ports/cppwork/vcpkg_installed/x64-osx/share )
elseif(UNIX)
message(STATUS "This is linux, set CMAKE_PREFIX_PATH.")
set(CMAKE_PREFIX_PATH /vcpkg/ports/cppwork/vcpkg_installed/x64-linux/share)
endif(APPLE)
project(binary_tree_it_coroutine)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing")
add_definitions(-g)
find_package(ZLIB)
find_package(OpenCV REQUIRED )
find_package(Arrow CONFIG REQUIRED)
find_package(unofficial-brotli REQUIRED)
find_package(unofficial-utf8proc CONFIG REQUIRED)
find_package(Thrift CONFIG REQUIRED)
find_package(glog REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(Boost REQUIRED COMPONENTS
system
filesystem
serialization
program_options
thread
)
find_package(DataFrame REQUIRED)
if(APPLE)
MESSAGE(STATUS "This is APPLE, set INCLUDE_DIRS")
set(INCLUDE_DIRS ${Boost_INCLUDE_DIRS} /usr/local/include /usr/local/iODBC/include /opt/snowflake/snowflakeodbc/include/ ${CMAKE_CURRENT_SOURCE_DIR}/../include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../../include)
elseif(UNIX)
MESSAGE(STATUS "This is linux, set INCLUDE_DIRS")
set(INCLUDE_DIRS ${Boost_INCLUDE_DIRS} /usr/local/include ${CMAKE_CURRENT_SOURCE_DIR}/../include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/)
endif(APPLE)
if(APPLE)
MESSAGE(STATUS "This is APPLE, set LINK_DIRS")
set(LINK_DIRS /usr/local/lib /usr/local/iODBC/lib /opt/snowflake/snowflakeodbc/lib/universal)
elseif(UNIX)
MESSAGE(STATUS "This is linux, set LINK_DIRS")
set(LINK_DIRS ${Boost_INCLUDE_DIRS} /usr/local/lib /vcpkg/ports/cppwork/vcpkg_installed/x64-linux/lib)
endif(APPLE)
if(APPLE)
MESSAGE(STATUS "This is APPLE, set ODBC_LIBS")
set(ODBC_LIBS iodbc iodbcinst)
elseif(UNIX)
MESSAGE(STATUS "This is linux, set LINK_DIRS")
set(ODBC_LIBS odbc odbcinst ltdl)
endif(APPLE)
include_directories(${INCLUDE_DIRS})
LINK_DIRECTORIES(${LINK_DIRS})
file( GLOB test_file_list ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/../include/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/arr_/impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/http/impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/yaml/impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/df/impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/death_handler/impl/*.cpp)
add_library(${PROJECT_NAME}_lib SHARED ${APP_SOURCES} ${test_file})
target_link_libraries(${PROJECT_NAME}_lib ${Boost_LIBRARIES} ZLIB::ZLIB glog::glog DataFrame::DataFrame ${OpenCV_LIBS})
target_link_libraries(${PROJECT_NAME}_lib OpenSSL::SSL OpenSSL::Crypto libgtest.a pystring libyaml-cpp.a libgmock.a ${ODBC_LIBS} libnanodbc.a pthread dl backtrace libzstd.a libbz2.a libsnappy.a re2::re2 parquet lz4 unofficial::brotli::brotlidec-static unofficial::brotli::brotlienc-static unofficial::brotli::brotlicommon-static utf8proc thrift::thrift arrow arrow_dataset)
foreach( test_file ${test_file_list} )
file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${test_file})
string(REPLACE ".cpp" "" file ${filename})
add_executable(${file} ${test_file})
target_link_libraries(${file} ${PROJECT_NAME}_lib)
endforeach( test_file ${test_file_list})
test/binary_tree_iterator_coroutine_test.cpp
#include "binary_tree_iterator_coroutine.hpp"
#include
#include
#include
#include
#include "death_handler/death_handler.h"
int main(int argc, char** argv) {
FLAGS_log_dir = "./";
FLAGS_alsologtostderr = true;
// 日志级别 INFO, WARNING, ERROR, FATAL 的值分别为0、1、2、3
FLAGS_minloglevel = 0;
Debug::DeathHandler dh;
google::InitGoogleLogging("./logs.log");
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
}
// 后根遍历迭代器
GTEST_TEST(BinaryTreeIteratorTests, BinaryTreeIt) {
BinaryTree family (
new Node("me",
new Node("mother",
new Node("Mother's mother"),
new Node("Mother's father")
),
new Node("father"))
);
for(auto it: family.post_order()) {
std::cout << (*it).value << "\n";
}
}
include/binary_tree_iterator_coroutine.hpp
#ifndef _FREDRIC_BINARY_TREE_ITERATOR_COROUTINE_HPP_
#define _FREDRIC_BINARY_TREE_ITERATOR_COROUTINE_HPP_
#include
#include
#include
#include
#include
#include
#include
template
struct BinaryTree;
template
struct Node {
T value{};
Node* left{nullptr}, *right{nullptr}, *parent{nullptr};
BinaryTree* tree{nullptr};
Node(T value_) : value{value_} {}
Node(T value_, Node* left_, Node* right_)
: value(value_), left(left_), right{right_} {
// 初始化左子节点和右子节点的树为同一颗树
this->left->tree = this->right->tree = tree;
// 初始化左子节点和右子节点的父节点为当前节点
this->left->parent = this->right->parent = this;
}
void set_tree(BinaryTree* t) {
tree = t;
if (left) left->set_tree(t);
if (right) right->set_tree(t);
}
~Node() {
if(left) delete left;
if(right) delete right;
}
};
template
struct BinaryTree {
Node* root{nullptr};
BinaryTree(Node* root_) : root(root_) {
root->set_tree(this);
}
~BinaryTree() {
if(root) {
delete root;
}
}
std::generator*> post_order() {
return post_order_impl(root);
}
private:
std::generator*> post_order_impl(Node* node) {
// 递归yield 一个node,左右根,后续遍历
if(node) {
for(auto x: post_order_impl(node->left)) {
co_yield x;
}
for(auto y: post_order_impl(node->right)) {
co_yield y;
}
co_yield node;
}
}
};
#endif
include/generator/generator头文件
#ifndef __STD_GENERATOR_INCLUDED
#define __STD_GENERATOR_INCLUDED
///
// Reference implementation of std::generator proposal P2168.
//
// See https://wg21.link/P2168 for details.
//
///
// Copyright Lewis Baker, Corentin Jabot
//
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0.
// (See accompanying file LICENSE or http://www.boost.org/LICENSE_1_0.txt)
///
#pragma once
#if __has_include()
#include
#else
// Fallback for older experimental implementations of coroutines.
#include
namespace std {
using std::experimental::coroutine_handle;
using std::experimental::coroutine_traits;
using std::experimental::noop_coroutine;
using std::experimental::suspend_always;
using std::experimental::suspend_never;
} // namespace std
#endif
#include
#include
#include
#include
#include
#include
#include
#if __has_include()
# include
#else
// Placeholder implementation of the bits we need from header
// when we don't have the header (e.g. Clang 12 and earlier).
namespace std {
// Don't create naming conflicts with recent libc++ which defines std::iter_reference_t
// in but doesn't yet provide a header.
template
using __iter_reference_t = decltype(*std::declval<_T&>());
template
using iter_value_t =
typename std::iterator_traits>::value_type;
namespace ranges {
namespace __begin {
void begin();
struct _fn {
template
requires requires(_Range& __r) {
__r.begin();
}
auto operator()(_Range&& __r) const
noexcept(noexcept(__r.begin()))
-> decltype(__r.begin()) {
return __r.begin();
}
template
requires
(!requires(_Range& __r) { __r.begin(); }) &&
requires(_Range& __r) { begin(__r); }
auto operator()(_Range&& __r) const
noexcept(noexcept(begin(__r)))
-> decltype(begin(__r)) {
return begin(__r);
}
};
} // namespace __begin
inline namespace __begin_cpo {
inline constexpr __begin::_fn begin = {};
}
namespace __end {
void end();
struct _fn {
template
requires requires(_Range& __r) { __r.end(); }
auto operator()(_Range&& __r) const
noexcept(noexcept(__r.end()))
-> decltype(__r.end()) {
return __r.end();
}
template
requires
(!requires(_Range& __r) { __r.end(); }) &&
requires(_Range& __r) { end(__r); }
auto operator()(_Range&& __r) const
noexcept(noexcept(end(__r)))
-> decltype(end(__r)) {
return end(__r);
}
};
} // namespace __end
inline namespace _end_cpo {
inline constexpr __end::_fn end = {};
}
template
using iterator_t = decltype(begin(std::declval<_Range>()));
template
using sentinel_t = decltype(end(std::declval<_Range>()));
template
using range_reference_t = __iter_reference_t>;
template
using range_value_t = iter_value_t>;
template
concept range = requires(_T& __t) {
ranges::begin(__t);
ranges::end(__t);
};
} // namespace ranges
} // namespace std
#endif // !__has_include()
namespace std {
template
class __manual_lifetime {
public:
__manual_lifetime() noexcept {}
~__manual_lifetime() {}
template
_T& construct(_Args&&... __args) noexcept(std::is_nothrow_constructible_v<_T, _Args...>) {
return *::new (static_cast(std::addressof(__value_))) _T((_Args&&)__args...);
}
void destruct() noexcept(std::is_nothrow_destructible_v<_T>) {
__value_.~_T();
}
_T& get() & noexcept {
return __value_;
}
_T&& get() && noexcept {
return static_cast<_T&&>(__value_);
}
const _T& get() const & noexcept {
return __value_;
}
const _T&& get() const && noexcept {
return static_cast(__value_);
}
private:
union {
std::remove_const_t<_T> __value_;
};
};
template
class __manual_lifetime<_T&> {
public:
__manual_lifetime() noexcept : __value_(nullptr) {}
~__manual_lifetime() {}
_T& construct(_T& __value) noexcept {
__value_ = std::addressof(__value);
return __value;
}
void destruct() noexcept {}
_T& get() const noexcept {
return *__value_;
}
private:
_T* __value_;
};
template
class __manual_lifetime<_T&&> {
public:
__manual_lifetime() noexcept : __value_(nullptr) {}
~__manual_lifetime() {}
_T&& construct(_T&& __value) noexcept {
__value_ = std::addressof(__value);
return static_cast<_T&&>(__value);
}
void destruct() noexcept {}
_T&& get() const noexcept {
return static_cast<_T&&>(*__value_);
}
private:
_T* __value_;
};
struct use_allocator_arg {};
namespace ranges {
template
struct elements_of {
explicit constexpr elements_of(_Rng&& __rng) noexcept
requires std::is_default_constructible_v<_Allocator>
: __range(static_cast<_Rng&&>(__rng))
{}
constexpr elements_of(_Rng&& __rng, _Allocator&& __alloc) noexcept
: __range((_Rng&&)__rng), __alloc((_Allocator&&)__alloc) {}
constexpr elements_of(elements_of&&) noexcept = default;
constexpr elements_of(const elements_of &) = delete;
constexpr elements_of &operator=(const elements_of &) = delete;
constexpr elements_of &operator=(elements_of &&) = delete;
constexpr _Rng&& get() noexcept {
return static_cast<_Rng&&>(__range);
}
constexpr _Allocator get_allocator() const noexcept {
return __alloc;
}
private:
[[no_unique_address]] _Allocator __alloc; // \expos
_Rng && __range; // \expos
};
template
elements_of(_Rng &&) -> elements_of<_Rng>;
template
elements_of(_Rng &&, Allocator&&) -> elements_of<_Rng, Allocator>;
} // namespace ranges
template
static constexpr bool __allocator_needs_to_be_stored =
!std::allocator_traits<_Alloc>::is_always_equal::value ||
!std::is_default_constructible_v<_Alloc>;
// Round s up to next multiple of a.
constexpr size_t __aligned_allocation_size(size_t s, size_t a) {
return (s + a - 1) & ~(a - 1);
}
template ,
typename _Allocator = use_allocator_arg>
class generator;
template
class __promise_base_alloc {
static constexpr std::size_t __offset_of_allocator(std::size_t __frameSize) noexcept {
return __aligned_allocation_size(__frameSize, alignof(_Alloc));
}
static constexpr std::size_t __padded_frame_size(std::size_t __frameSize) noexcept {
return __offset_of_allocator(__frameSize) + sizeof(_Alloc);
}
static _Alloc& __get_allocator(void* __frame, std::size_t __frameSize) noexcept {
return *reinterpret_cast<_Alloc*>(
static_cast(__frame) + __offset_of_allocator(__frameSize));
}
public:
template
static void* operator new(std::size_t __frameSize, std::allocator_arg_t, _Alloc __alloc, _Args&...) {
void* __frame = __alloc.allocate(__padded_frame_size(__frameSize));
// Store allocator at end of the coroutine frame.
// Assuming the allocator's move constructor is non-throwing (a requirement for allocators)
::new (static_cast(std::addressof(__get_allocator(__frame, __frameSize)))) _Alloc(std::move(__alloc));
return __frame;
}
template
static void* operator new(std::size_t __frameSize, _This&, std::allocator_arg_t, _Alloc __alloc, _Args&...) {
return __promise_base_alloc::operator new(__frameSize, std::allocator_arg, std::move(__alloc));
}
static void operator delete(void* __ptr, std::size_t __frameSize) noexcept {
_Alloc& __alloc = __get_allocator(__ptr, __frameSize);
_Alloc __localAlloc(std::move(__alloc));
__alloc.~Alloc();
__localAlloc.deallocate(static_cast(__ptr), __padded_frame_size(__frameSize));
}
};
template
requires (!__allocator_needs_to_be_stored<_Alloc>)
class __promise_base_alloc<_Alloc> {
public:
static void* operator new(std::size_t __size) {
_Alloc __alloc;
return __alloc.allocate(__size);
}
static void operator delete(void* __ptr, std::size_t __size) noexcept {
_Alloc __alloc;
__alloc.deallocate(static_cast(__ptr), __size);
}
};
template
struct __generator_promise_base
{
template
friend class generator;
__generator_promise_base* __root_;
std::coroutine_handle<> __parentOrLeaf_;
// Note: Using manual_lifetime here to avoid extra calls to exception_ptr
// constructor/destructor in cases where it is not needed (i.e. where this
// generator coroutine is not used as a nested coroutine).
// This member is lazily constructed by the __yield_sequence_awaiter::await_suspend()
// method if this generator is used as a nested generator.
__manual_lifetime __exception_;
__manual_lifetime<_Ref> __value_;
explicit __generator_promise_base(std::coroutine_handle<> thisCoro) noexcept
: __root_(this)
, __parentOrLeaf_(thisCoro)
{}
~__generator_promise_base() {
if (__root_ != this) {
// This coroutine was used as a nested generator and so will
// have constructed its __exception_ member which needs to be
// destroyed here.
__exception_.destruct();
}
}
std::suspend_always initial_suspend() noexcept {
return {};
}
void return_void() noexcept {}
void unhandled_exception() {
if (__root_ != this) {
__exception_.get() = std::current_exception();
} else {
throw;
}
}
// Transfers control back to the parent of a nested coroutine
struct __final_awaiter {
bool await_ready() noexcept {
return false;
}
template
std::coroutine_handle<>
await_suspend(std::coroutine_handle<_Promise> __h) noexcept {
_Promise& __promise = __h.promise();
__generator_promise_base& __root = *__promise.__root_;
if (&__root != &__promise) {
auto __parent = __promise.__parentOrLeaf_;
__root.__parentOrLeaf_ = __parent;
return __parent;
}
return std::noop_coroutine();
}
void await_resume() noexcept {}
};
__final_awaiter final_suspend() noexcept {
return {};
}
std::suspend_always yield_value(_Ref&& __x)
noexcept(std::is_nothrow_move_constructible_v<_Ref>) {
__root_->__value_.construct((_Ref&&)__x);
return {};
}
template
requires
(!std::is_reference_v<_Ref>) &&
std::is_convertible_v<_T, _Ref>
std::suspend_always yield_value(_T&& __x)
noexcept(std::is_nothrow_constructible_v<_Ref, _T>) {
__root_->__value_.construct((_T&&)__x);
return {};
}
template
struct __yield_sequence_awaiter {
_Gen __gen_;
__yield_sequence_awaiter(_Gen&& __g) noexcept
// Taking ownership of the generator ensures frame are destroyed
// in the reverse order of their execution.
: __gen_((_Gen&&)__g) {
}
bool await_ready() noexcept {
return false;
}
// set the parent, root and exceptions pointer and
// resume the nested
template
std::coroutine_handle<>
await_suspend(std::coroutine_handle<_Promise> __h) noexcept {
__generator_promise_base& __current = __h.promise();
__generator_promise_base& __nested = *__gen_.__get_promise();
__generator_promise_base& __root = *__current.__root_;
__nested.__root_ = __current.__root_;
__nested.__parentOrLeaf_ = __h;
// Lazily construct the __exception_ member here now that we
// know it will be used as a nested generator. This will be
// destroyed by the promise destructor.
__nested.__exception_.construct();
__root.__parentOrLeaf_ = __gen_.__get_coro();
// Immediately resume the nested coroutine (nested generator)
return __gen_.__get_coro();
}
void await_resume() {
__generator_promise_base& __nestedPromise = *__gen_.__get_promise();
if (__nestedPromise.__exception_.get()) {
std::rethrow_exception(std::move(__nestedPromise.__exception_.get()));
}
}
};
template
__yield_sequence_awaiter>
yield_value(std::ranges::elements_of> __g) noexcept {
return std::move(__g).get();
}
template
__yield_sequence_awaiter, _Allocator>>
yield_value(std::ranges::elements_of<_Rng, _Allocator> && __x) {
return [](allocator_arg_t, _Allocator alloc, auto && __rng) -> generator<_Ref, std::remove_cvref_t<_Ref>, _Allocator> {
for(auto && e: __rng)
co_yield static_cast(e);
}(std::allocator_arg, __x.get_allocator(), std::forward<_Rng>(__x.get()));
}
void resume() {
__parentOrLeaf_.resume();
}
// Disable use of co_await within this coroutine.
void await_transform() = delete;
};
template
struct __generator_promise;
template
struct __generator_promise, _ByteAllocator, _ExplicitAllocator> final
: public __generator_promise_base<_Ref>
, public __promise_base_alloc<_ByteAllocator> {
__generator_promise() noexcept
: __generator_promise_base<_Ref>(std::coroutine_handle<__generator_promise>::from_promise(*this))
{}
generator<_Ref, _Value, _Alloc> get_return_object() noexcept {
return generator<_Ref, _Value, _Alloc>{
std::coroutine_handle<__generator_promise>::from_promise(*this)
};
}
using __generator_promise_base<_Ref>::yield_value;
template
typename __generator_promise_base<_Ref>::template __yield_sequence_awaiter>
yield_value(std::ranges::elements_of<_Rng> && __x) {
static_assert (!_ExplicitAllocator,
"This coroutine has an explicit allocator specified with std::allocator_arg so an allocator needs to be passed "
"explicitely to std::elements_of");
return [](auto && __rng) -> generator<_Ref, _Value, _Alloc> {
for(auto && e: __rng)
co_yield static_cast(e);
}(std::forward<_Rng>(__x.get()));
}
};
template
using __byte_allocator_t = typename std::allocator_traits>::template rebind_alloc;
// Type-erased allocator with default allocator behaviour.
template
struct coroutine_traits, _Args...> {
using promise_type = __generator_promise, std::allocator>;
};
// Type-erased allocator with std::allocator_arg parameter
template
struct coroutine_traits, allocator_arg_t, _Alloc, _Args...> {
private:
using __byte_allocator = __byte_allocator_t<_Alloc>;
public:
using promise_type = __generator_promise, __byte_allocator, true /*explicit Allocator*/>;
};
// Type-erased allocator with std::allocator_arg parameter (non-static member functions)
template
struct coroutine_traits, _This, allocator_arg_t, _Alloc, _Args...> {
private:
using __byte_allocator = __byte_allocator_t<_Alloc>;
public:
using promise_type = __generator_promise, __byte_allocator, true /*explicit Allocator*/>;
};
// Generator with specified allocator type
template
struct coroutine_traits, _Args...> {
using __byte_allocator = __byte_allocator_t<_Alloc>;
public:
using promise_type = __generator_promise, __byte_allocator>;
};
// TODO : make layout compatible promise casts possible
template
class generator {
using __byte_allocator = __byte_allocator_t<_Alloc>;
public:
using promise_type = __generator_promise, __byte_allocator>;
friend promise_type;
private:
using __coroutine_handle = std::coroutine_handle;
public:
generator() noexcept = default;
generator(generator&& __other) noexcept
: __coro_(std::exchange(__other.__coro_, {}))
, __started_(std::exchange(__other.__started_, false)) {
}
~generator() noexcept {
if (__coro_) {
if (__started_ && !__coro_.done()) {
__coro_.promise().__value_.destruct();
}
__coro_.destroy();
}
}
generator& operator=(generator && g) noexcept {
swap(g);
return *this;
}
void swap(generator& __other) noexcept {
std::swap(__coro_, __other.__coro_);
std::swap(__started_, __other.__started_);
}
struct sentinel {};
class iterator {
public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = _Value;
using reference = _Ref;
using pointer = std::add_pointer_t<_Ref>;
iterator() noexcept = default;
iterator(const iterator &) = delete;
iterator(iterator&& __other) noexcept
: __coro_(std::exchange(__other.__coro_, {})) {
}
iterator& operator=(iterator&& __other) {
std::swap(__coro_, __other.__coro_);
return *this;
}
~iterator() {
}
friend bool operator==(const iterator &it, sentinel) noexcept {
return it.__coro_.done();
}
iterator &operator++() {
__coro_.promise().__value_.destruct();
__coro_.promise().resume();
return *this;
}
void operator++(int) {
(void)operator++();
}
reference operator*() const noexcept {
return static_cast(__coro_.promise().__value_.get());
}
private:
friend generator;
explicit iterator(__coroutine_handle __coro) noexcept
: __coro_(__coro) {}
__coroutine_handle __coro_;
};
iterator begin() {
assert(__coro_);
assert(!__started_);
__started_ = true;
__coro_.resume();
return iterator{__coro_};
}
sentinel end() noexcept {
return {};
}
private:
explicit generator(__coroutine_handle __coro) noexcept
: __coro_(__coro) {
}
public: // to get around access restrictions for __yield_sequence_awaitable
std::coroutine_handle<> __get_coro() noexcept { return __coro_; }
promise_type* __get_promise() noexcept { return std::addressof(__coro_.promise()); }
private:
__coroutine_handle __coro_;
bool __started_ = false;
};
// Specialisation for type-erased allocator implementation.
template
class generator<_Ref, _Value, use_allocator_arg> {
using __promise_base = __generator_promise_base<_Ref>;
public:
generator() noexcept
: __promise_(nullptr)
, __coro_()
, __started_(false)
{}
generator(generator&& __other) noexcept
: __promise_(std::exchange(__other.__promise_, nullptr))
, __coro_(std::exchange(__other.__coro_, {}))
, __started_(std::exchange(__other.__started_, false)) {
}
~generator() noexcept {
if (__coro_) {
if (__started_ && !__coro_.done()) {
__promise_->__value_.destruct();
}
__coro_.destroy();
}
}
generator& operator=(generator g) noexcept {
swap(g);
return *this;
}
void swap(generator& __other) noexcept {
std::swap(__promise_, __other.__promise_);
std::swap(__coro_, __other.__coro_);
std::swap(__started_, __other.__started_);
}
struct sentinel {};
class iterator {
public:
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = _Value;
using reference = _Ref;
using pointer = std::add_pointer_t<_Ref>;
iterator() noexcept = default;
iterator(const iterator &) = delete;
iterator(iterator&& __other) noexcept
: __promise_(std::exchange(__other.__promise_, nullptr))
, __coro_(std::exchange(__other.__coro_, {}))
{}
iterator& operator=(iterator&& __other) {
__promise_ = std::exchange(__other.__promise_, nullptr);
__coro_ = std::exchange(__other.__coro_, {});
return *this;
}
~iterator() = default;
friend bool operator==(const iterator &it, sentinel) noexcept {
return it.__coro_.done();
}
iterator& operator++() {
__promise_->__value_.destruct();
__promise_->resume();
return *this;
}
void operator++(int) {
(void)operator++();
}
reference operator*() const noexcept {
return static_cast(__promise_->__value_.get());
}
private:
friend generator;
explicit iterator(__promise_base* __promise, std::coroutine_handle<> __coro) noexcept
: __promise_(__promise)
, __coro_(__coro)
{}
__promise_base* __promise_;
std::coroutine_handle<> __coro_;
};
iterator begin() {
assert(__coro_);
assert(!__started_);
__started_ = true;
__coro_.resume();
return iterator{__promise_, __coro_};
}
sentinel end() noexcept {
return {};
}
private:
template
friend struct __generator_promise;
template
explicit generator(std::coroutine_handle<_Promise> __coro) noexcept
: __promise_(std::addressof(__coro.promise()))
, __coro_(__coro)
{}
public: // to get around access restrictions for __yield_sequence_awaitable
std::coroutine_handle<> __get_coro() noexcept { return __coro_; }
__promise_base* __get_promise() noexcept { return __promise_; }
private:
__promise_base* __promise_;
std::coroutine_handle<> __coro_;
bool __started_ = false;
};
#if __has_include()
namespace ranges {
template
constexpr inline bool enable_view> = true;
} // namespace ranges
#endif
} // namespace std
#endif // __STD_GENERATOR_INCLUDED