aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.generator
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.generator')
-rw-r--r--nihil.generator/CMakeLists.txt7
-rw-r--r--nihil.generator/byte_allocator.ccm27
-rw-r--r--nihil.generator/coroutine_traits.ccm70
-rw-r--r--nihil.generator/forward.ccm29
-rw-r--r--nihil.generator/generator.ccm717
-rw-r--r--nihil.generator/generator.test.cc (renamed from nihil.generator/test.cc)0
-rw-r--r--nihil.generator/generator_promise.ccm65
-rw-r--r--nihil.generator/generator_promise_base.ccm205
-rw-r--r--nihil.generator/manual_lifetime.ccm8
-rw-r--r--nihil.generator/nihil.generator.ccm7
10 files changed, 680 insertions, 455 deletions
diff --git a/nihil.generator/CMakeLists.txt b/nihil.generator/CMakeLists.txt
index 7464785..e521159 100644
--- a/nihil.generator/CMakeLists.txt
+++ b/nihil.generator/CMakeLists.txt
@@ -6,7 +6,12 @@ target_sources(nihil.generator
nihil.generator.ccm
generator.ccm
+ byte_allocator.ccm
+ coroutine_traits.ccm
elements_of.ccm
+ forward.ccm
+ generator_promise_base.ccm
+ generator_promise.ccm
manual_lifetime.ccm
promise_base_alloc.ccm
util.ccm
@@ -15,7 +20,7 @@ target_sources(nihil.generator
if(NIHIL_TESTS)
enable_testing()
- add_executable(nihil.generator.test test.cc)
+ add_executable(nihil.generator.test generator.test.cc)
target_link_libraries(nihil.generator.test PRIVATE
nihil.generator
Catch2::Catch2WithMain
diff --git a/nihil.generator/byte_allocator.ccm b/nihil.generator/byte_allocator.ccm
new file mode 100644
index 0000000..6d46ec6
--- /dev/null
+++ b/nihil.generator/byte_allocator.ccm
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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)
+///////////////////////////////////////////////////////////////////////////////
+
+module;
+
+#include <memory>
+#include <type_traits>
+
+export module nihil.generator:byte_allocator;
+
+namespace nihil {
+
+template <typename Alloc>
+using byte_allocator_t = typename std::allocator_traits<
+ std::remove_cvref_t<Alloc>>::template rebind_alloc<std::byte>;
+
+} // namespace nihil
diff --git a/nihil.generator/coroutine_traits.ccm b/nihil.generator/coroutine_traits.ccm
new file mode 100644
index 0000000..2a9d51d
--- /dev/null
+++ b/nihil.generator/coroutine_traits.ccm
@@ -0,0 +1,70 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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)
+///////////////////////////////////////////////////////////////////////////////
+
+module;
+
+#include <coroutine>
+#include <memory>
+
+export module nihil.generator:coroutine_traits;
+
+import :byte_allocator;
+import :forward;
+import :generator_promise;
+
+namespace std {
+
+// Type-erased allocator with default allocator behaviour.
+export template <typename Ref, typename Value, typename... Args>
+struct coroutine_traits<nihil::generator<Ref, Value>, Args...>
+{
+ using promise_type =
+ nihil::generator_promise<nihil::generator<Ref, Value>, std::allocator<std::byte>>;
+};
+
+// Type-erased allocator with std::allocator_arg parameter
+export template <typename Ref, typename Value, typename Alloc, typename... Args>
+struct coroutine_traits<nihil::generator<Ref, Value>, allocator_arg_t, Alloc, Args...>
+{
+private:
+ using byte_allocator = nihil::byte_allocator_t<Alloc>;
+
+public:
+ using promise_type = nihil::generator_promise<nihil::generator<Ref, Value>, byte_allocator,
+ true /*explicit Allocator*/>;
+};
+
+// Type-erased allocator with std::allocator_arg parameter (non-static member functions)
+export template <typename Ref, typename Value, typename This, typename Alloc, typename... Args>
+struct coroutine_traits<nihil::generator<Ref, Value>, This, allocator_arg_t, Alloc, Args...>
+{
+private:
+ using byte_allocator = nihil::byte_allocator_t<Alloc>;
+
+public:
+ using promise_type = nihil::generator_promise<nihil::generator<Ref, Value>, byte_allocator,
+ true /*explicit Allocator*/>;
+};
+
+// Generator with specified allocator type
+export template <typename Ref, typename Value, typename Alloc, typename... Args>
+struct coroutine_traits<nihil::generator<Ref, Value, Alloc>, Args...>
+{
+ using byte_allocator = nihil::byte_allocator_t<Alloc>;
+
+public:
+ using promise_type =
+ nihil::generator_promise<nihil::generator<Ref, Value, Alloc>, byte_allocator>;
+};
+
+} // namespace std
diff --git a/nihil.generator/forward.ccm b/nihil.generator/forward.ccm
new file mode 100644
index 0000000..8d5ca4d
--- /dev/null
+++ b/nihil.generator/forward.ccm
@@ -0,0 +1,29 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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)
+///////////////////////////////////////////////////////////////////////////////
+
+module;
+
+#include <type_traits>
+
+export module nihil.generator:forward;
+
+import :util;
+
+namespace nihil {
+
+export template <typename Ref,
+ typename Value = std::remove_cvref_t<Ref>,
+ typename Allocator = use_allocator_arg>
+class generator;
+
+} // namespace nihil
diff --git a/nihil.generator/generator.ccm b/nihil.generator/generator.ccm
index 27e8103..96790a8 100644
--- a/nihil.generator/generator.ccm
+++ b/nihil.generator/generator.ccm
@@ -23,485 +23,306 @@ module;
export module nihil.generator:generator;
+import :byte_allocator;
+import :coroutine_traits;
import :elements_of;
+import :forward;
+import :generator_promise_base;
+import :generator_promise;
import :manual_lifetime;
import :promise_base_alloc;
import :util;
namespace nihil {
-export template <typename _Ref,
- typename _Value = std::remove_cvref_t<_Ref>,
- typename _Allocator = use_allocator_arg>
-class generator;
-
-
-template<typename _Ref>
-struct __generator_promise_base
+// TODO : make layout compatible promise casts possible
+export template <typename Ref, typename Value, typename Alloc>
+class generator
{
- template <typename _Ref2, typename _Value, typename _Alloc>
- 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<std::exception_ptr> __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 <typename _Promise>
- 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 <typename _T>
- 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 <typename _Gen>
- 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<typename _Promise>
- 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 <typename _OValue, typename _OAlloc>
- __yield_sequence_awaiter<generator<_Ref, _OValue, _OAlloc>>
- yield_value(nihil::elements_of<generator<_Ref, _OValue, _OAlloc>> __g) noexcept {
- return std::move(__g).get();
- }
-
- template <std::ranges::range _Rng, typename _Allocator>
- __yield_sequence_awaiter<generator<_Ref, std::remove_cvref_t<_Ref>, _Allocator>>
- yield_value(nihil::elements_of<_Rng, _Allocator> && __x) {
- return [](std::allocator_arg_t, _Allocator, auto && __rng) -> generator<_Ref, std::remove_cvref_t<_Ref>, _Allocator> {
- for(auto && e: __rng)
- co_yield static_cast<decltype(e)>(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;
-};
+ using byte_allocator = byte_allocator_t<Alloc>;
-template<typename _Generator, typename _ByteAllocator, bool _ExplicitAllocator = false>
-struct __generator_promise;
-
-template<typename _Ref, typename _Value, typename _Alloc, typename _ByteAllocator, bool _ExplicitAllocator>
-struct __generator_promise<generator<_Ref, _Value, _Alloc>, _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 <std::ranges::range _Rng>
- typename __generator_promise_base<_Ref>::template __yield_sequence_awaiter<generator<_Ref, _Value, _Alloc>>
- yield_value(nihil::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<decltype(e)>(e);
- }(std::forward<_Rng>(__x.get()));
- }
-};
-
-template<typename _Alloc>
-using __byte_allocator_t = typename std::allocator_traits<std::remove_cvref_t<_Alloc>>::template rebind_alloc<std::byte>;
-
-} // namespace nihil
-
-namespace std {
-
-// Type-erased allocator with default allocator behaviour.
-export template<typename _Ref, typename _Value, typename... _Args>
-struct coroutine_traits<nihil::generator<_Ref, _Value>, _Args...> {
- using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value>, std::allocator<std::byte>>;
-};
-
-// Type-erased allocator with std::allocator_arg parameter
-export template<typename _Ref, typename _Value, typename _Alloc, typename... _Args>
-struct coroutine_traits<nihil::generator<_Ref, _Value>, allocator_arg_t, _Alloc, _Args...> {
-private:
- using __byte_allocator = nihil::__byte_allocator_t<_Alloc>;
public:
- using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value>, __byte_allocator, true /*explicit Allocator*/>;
-};
+ using promise_type = generator_promise<generator<Ref, Value, Alloc>, byte_allocator>;
+ friend promise_type;
-// Type-erased allocator with std::allocator_arg parameter (non-static member functions)
-export template<typename _Ref, typename _Value, typename _This, typename _Alloc, typename... _Args>
-struct coroutine_traits<nihil::generator<_Ref, _Value>, _This, allocator_arg_t, _Alloc, _Args...> {
private:
- using __byte_allocator = nihil::__byte_allocator_t<_Alloc>;
-public:
- using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value>, __byte_allocator, true /*explicit Allocator*/>;
-};
+ using coroutine_handle = std::coroutine_handle<promise_type>;
-// Generator with specified allocator type
-export template<typename _Ref, typename _Value, typename _Alloc, typename... _Args>
-struct coroutine_traits<nihil::generator<_Ref, _Value, _Alloc>, _Args...> {
- using __byte_allocator = nihil::__byte_allocator_t<_Alloc>;
public:
- using promise_type = nihil::__generator_promise<nihil::generator<_Ref, _Value, _Alloc>, __byte_allocator>;
-};
-
-} // namespace std
-
-namespace nihil {
+ generator() noexcept = default;
+
+ generator(generator &&other) noexcept
+ : m_coro(std::exchange(other.m_coro, {}))
+ , m_started(std::exchange(other.m_started, false))
+ {
+ }
+
+ ~generator() noexcept
+ {
+ if (m_coro) {
+ if (m_started && !m_coro.done()) {
+ m_coro.promise().__value_.destruct();
+ }
+ m_coro.destroy();
+ }
+ }
+
+ auto operator=(generator &&g) noexcept -> generator &
+ {
+ swap(g);
+ return *this;
+ }
+
+ void swap(generator &other) noexcept
+ {
+ std::swap(m_coro, other.m_coro);
+ std::swap(m_started, other.m_started);
+ }
+
+ struct sentinel
+ {
+ };
+
+ struct iterator
+ {
+ 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
+ : m_coro(std::exchange(other.m_coro, {}))
+ {
+ }
+
+ auto operator=(iterator &&other) noexcept -> iterator &
+ {
+ std::swap(m_coro, other.m_coro);
+ return *this;
+ }
+
+ ~iterator() = default;
+
+ friend auto operator==(const iterator &it, sentinel) noexcept -> bool
+ {
+ return it.m_coro.done();
+ }
+
+ auto operator++() -> iterator &
+ {
+ m_coro.promise().m_value.destruct();
+ m_coro.promise().resume();
+ return *this;
+ }
+
+ void operator++(int)
+ {
+ (void)operator++();
+ }
+
+ auto operator*() const noexcept -> reference
+ {
+ return static_cast<reference>(m_coro.promise().m_value.get());
+ }
+
+ private:
+ friend generator;
+
+ explicit iterator(coroutine_handle coro) noexcept
+ : m_coro(coro)
+ {
+ }
+
+ coroutine_handle m_coro;
+ };
+
+ auto begin() -> iterator
+ {
+ assert(m_coro);
+ assert(!m_started);
+ m_started = true;
+ m_coro.resume();
+ return iterator{m_coro};
+ }
+
+ auto end() noexcept -> sentinel
+ {
+ return {};
+ }
-// TODO : make layout compatible promise casts possible
-export template <typename _Ref, typename _Value, typename _Alloc>
-class generator {
- using __byte_allocator = __byte_allocator_t<_Alloc>;
-public:
- using promise_type = __generator_promise<generator<_Ref, _Value, _Alloc>, __byte_allocator>;
- friend promise_type;
private:
- using __coroutine_handle = std::coroutine_handle<promise_type>;
-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<reference>(__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) {
- }
+ explicit generator(coroutine_handle coro) noexcept
+ : m_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()); }
+ auto get_coro() noexcept -> std::coroutine_handle<>
+ {
+ return m_coro;
+ }
+ auto get_promise() noexcept -> promise_type *
+ {
+ return std::addressof(m_coro.promise());
+ }
private:
- __coroutine_handle __coro_;
- bool __started_ = false;
+ coroutine_handle m_coro;
+ bool m_started = false;
};
// Specialisation for type-erased allocator implementation.
-export template <typename _Ref, typename _Value>
-class generator<_Ref, _Value, use_allocator_arg> {
- using __promise_base = __generator_promise_base<_Ref>;
-public:
+export template <typename Ref, typename Value>
+class generator<Ref, Value, use_allocator_arg>
+{
+ using promise_base = generator_promise_base<Ref>;
- 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<reference>(__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 {};
- }
+public:
+ generator() noexcept
+ : m_promise(nullptr)
+ {
+ }
+
+ generator(generator &&other) noexcept
+ : m_promise(std::exchange(other.m_promise, nullptr))
+ , m_coro(std::exchange(other.m_coro, {}))
+ , m_started(std::exchange(other.m_started, false))
+ {
+ }
+
+ ~generator() noexcept
+ {
+ if (m_coro) {
+ if (m_started && !m_coro.done()) {
+ m_promise->m_value.destruct();
+ }
+ m_coro.destroy();
+ }
+ }
+
+ auto operator=(generator g) noexcept -> generator &
+ {
+ swap(g);
+ return *this;
+ }
+
+ void swap(generator &other) noexcept
+ {
+ std::swap(m_promise, other.m_promise);
+ std::swap(m_coro, other.m_coro);
+ std::swap(m_started, other.m_started);
+ }
+
+ struct sentinel
+ {
+ };
+
+ struct iterator
+ {
+ 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
+ : m_promise(std::exchange(other.m_promise, nullptr))
+ , m_coro(std::exchange(other.m_coro, {}))
+ {
+ }
+
+ auto operator=(iterator &&other) noexcept -> iterator &
+ {
+ m_promise = std::exchange(other.m_promise, nullptr);
+ m_coro = std::exchange(other.m_coro, {});
+ return *this;
+ }
+
+ ~iterator() = default;
+
+ friend auto operator==(const iterator &it, sentinel) noexcept -> bool
+ {
+ return it.m_coro.done();
+ }
+
+ auto operator++() -> iterator &
+ {
+ m_promise->m_value.destruct();
+ m_promise->resume();
+ return *this;
+ }
+
+ void operator++(int)
+ {
+ (void)operator++();
+ }
+
+ auto operator*() const noexcept -> reference
+ {
+ return static_cast<reference>(m_promise->m_value.get());
+ }
+
+ private:
+ friend generator;
+
+ explicit iterator(promise_base *promise,
+ std::coroutine_handle<> coro) noexcept
+ : m_promise(promise)
+ , m_coro(coro)
+ {
+ }
+
+ promise_base *m_promise;
+ std::coroutine_handle<> m_coro;
+ };
+
+ auto begin() -> iterator
+ {
+ assert(m_coro);
+ assert(!m_started);
+ m_started = true;
+ m_coro.resume();
+ return iterator{m_promise, m_coro};
+ }
+
+ auto end() noexcept -> sentinel
+ {
+ return {};
+ }
private:
- template<typename _Generator, typename _ByteAllocator, bool _ExplicitAllocator>
- friend struct __generator_promise;
+ template <typename Generator, typename ByteAllocator, bool ExplicitAllocator>
+ friend struct generator_promise;
- template<typename _Promise>
- explicit generator(std::coroutine_handle<_Promise> __coro) noexcept
- : __promise_(std::addressof(__coro.promise()))
- , __coro_(__coro)
- {}
+ template <typename Promise>
+ explicit generator(std::coroutine_handle<Promise> coro) noexcept
+ : m_promise(std::addressof(coro.promise()))
+ , m_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_; }
+ auto get_coro() noexcept -> std::coroutine_handle<>
+ {
+ return m_coro;
+ }
+
+ auto get_promise() noexcept -> promise_base *
+ {
+ return m_promise;
+ }
private:
- __promise_base* __promise_;
- std::coroutine_handle<> __coro_;
- bool __started_ = false;
+ promise_base *m_promise;
+ std::coroutine_handle<> m_coro;
+ bool m_started = false;
};
} // namespace nihil
diff --git a/nihil.generator/test.cc b/nihil.generator/generator.test.cc
index 49272b4..49272b4 100644
--- a/nihil.generator/test.cc
+++ b/nihil.generator/generator.test.cc
diff --git a/nihil.generator/generator_promise.ccm b/nihil.generator/generator_promise.ccm
new file mode 100644
index 0000000..b0fd4b1
--- /dev/null
+++ b/nihil.generator/generator_promise.ccm
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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)
+///////////////////////////////////////////////////////////////////////////////
+
+module;
+
+#include <coroutine>
+#include <ranges>
+
+export module nihil.generator:generator_promise;
+
+import :forward;
+import :generator_promise_base;
+import :promise_base_alloc;
+
+namespace nihil {
+
+export template <typename Generator, typename ByteAllocator, bool ExplicitAllocator = false>
+struct generator_promise;
+
+export template <typename Ref, typename Value, typename Alloc, typename ByteAllocator,
+ bool ExplicitAllocator>
+struct generator_promise<generator<Ref, Value, Alloc>, 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))
+ {
+ }
+
+ auto get_return_object() noexcept -> generator<Ref, Value, Alloc>
+ {
+ return generator<Ref, Value, Alloc>{
+ std::coroutine_handle<generator_promise>::from_promise(*this)};
+ }
+
+ using generator_promise_base<Ref>::yield_value;
+
+ template <std::ranges::range Rng>
+ auto yield_value(nihil::elements_of<Rng> &&x) -> typename generator_promise_base<
+ Ref>::template yield_sequence_awaiter<generator<Ref, Value, Alloc>>
+ {
+ 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<decltype(e)>(e);
+ }(std::forward<Rng>(x.get()));
+ }
+};
+
+} // namespace nihil
diff --git a/nihil.generator/generator_promise_base.ccm b/nihil.generator/generator_promise_base.ccm
new file mode 100644
index 0000000..fec9b1b
--- /dev/null
+++ b/nihil.generator/generator_promise_base.ccm
@@ -0,0 +1,205 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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)
+///////////////////////////////////////////////////////////////////////////////
+
+module;
+
+#include <coroutine>
+#include <exception>
+#include <memory>
+
+export module nihil.generator:generator_promise_base;
+
+import :elements_of;
+import :forward;
+import :manual_lifetime;
+
+namespace nihil {
+
+template <typename Ref>
+struct generator_promise_base
+{
+ template <typename Ref2, typename Value, typename Alloc>
+ friend class generator;
+
+ generator_promise_base *m_root;
+ std::coroutine_handle<> m_parent_or_leaf;
+
+ // 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<std::exception_ptr> m_exception;
+ manual_lifetime<Ref> m_value;
+
+ explicit generator_promise_base(std::coroutine_handle<> this_coro) noexcept
+ : m_root(this)
+ , m_parent_or_leaf(this_coro)
+ {
+ }
+
+ ~generator_promise_base()
+ {
+ if (m_root != this) {
+ // This coroutine was used as a nested generator and so will
+ // have constructed its __exception_ member which needs to be
+ // destroyed here.
+ m_exception.destruct();
+ }
+ }
+
+ auto initial_suspend() noexcept -> std::suspend_always
+ {
+ return {};
+ }
+
+ auto return_void() noexcept -> void
+ {
+ }
+
+ auto unhandled_exception() -> void
+ {
+ if (m_root != this)
+ m_exception.get() = std::current_exception();
+ else
+ throw;
+ }
+
+ // Transfers control back to the parent of a nested coroutine
+ struct final_awaiter
+ {
+ auto await_ready() noexcept -> bool
+ {
+ return false;
+ }
+
+ template <typename Promise>
+ auto
+ await_suspend(std::coroutine_handle<Promise> h) noexcept -> std::coroutine_handle<>
+ {
+ Promise &promise = h.promise();
+ generator_promise_base &root = *promise.m_root;
+ if (&root != &promise) {
+ auto parent = promise.m_parent_or_leaf;
+ root.m_parent_or_leaf = parent;
+ return parent;
+ }
+ return std::noop_coroutine();
+ }
+
+ void await_resume() noexcept
+ {
+ }
+ };
+
+ auto final_suspend() noexcept -> final_awaiter
+ {
+ return {};
+ }
+
+ auto yield_value(Ref &&x) noexcept(std::is_nothrow_move_constructible_v<Ref>)
+ -> std::suspend_always
+ {
+ m_root->m_value.construct((Ref &&)x);
+ return {};
+ }
+
+ template <typename T>
+ requires(!std::is_reference_v<Ref>) && std::is_convertible_v<T, Ref>
+ auto
+ yield_value(T &&x) noexcept(std::is_nothrow_constructible_v<Ref, T>) -> std::suspend_always
+ {
+ m_root->m_value.construct((T &&)x);
+ return {};
+ }
+
+ template <typename Gen>
+ struct yield_sequence_awaiter
+ {
+ Gen m_gen;
+
+ yield_sequence_awaiter(Gen &&g) noexcept
+ // Taking ownership of the generator ensures frame are destroyed
+ // in the reverse order of their execution.
+ : m_gen((Gen &&)g)
+ {
+ }
+
+ auto await_ready() noexcept -> bool
+ {
+ return false;
+ }
+
+ // set the parent, root and exceptions pointer and
+ // resume the nested
+ template <typename Promise>
+ auto
+ await_suspend(std::coroutine_handle<Promise> h) noexcept -> std::coroutine_handle<>
+ {
+ generator_promise_base &current = h.promise();
+ generator_promise_base &nested = *m_gen.get_promise();
+ generator_promise_base &root = *current.m_root;
+
+ nested.m_root = current.m_root;
+ nested.m_parent_or_leaf = 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.m_exception.construct();
+ root.m_parent_or_leaf = m_gen.get_coro();
+
+ // Immediately resume the nested coroutine (nested generator)
+ return m_gen.get_coro();
+ }
+
+ void await_resume()
+ {
+ generator_promise_base &nested_promise = *m_gen.get_promise();
+
+ if (nested_promise.m_exception.get()) {
+ std::rethrow_exception(nested_promise.m_exception.get());
+ }
+ }
+ };
+
+ template <typename OValue, typename OAlloc>
+ auto yield_value(nihil::elements_of<generator<Ref, OValue, OAlloc>> g) noexcept
+ -> yield_sequence_awaiter<generator<Ref, OValue, OAlloc>>
+
+ {
+ return std::move(g).get();
+ }
+
+ template <std::ranges::range Rng, typename Allocator>
+ auto yield_value(nihil::elements_of<Rng, Allocator> &&x)
+ -> yield_sequence_awaiter<generator<Ref, std::remove_cvref_t<Ref>, Allocator>>
+
+ {
+ return [](std::allocator_arg_t, Allocator,
+ auto &&rng) -> generator<Ref, std::remove_cvref_t<Ref>, Allocator> {
+ for (auto &&e : rng)
+ co_yield static_cast<decltype(e)>(e);
+ }(std::allocator_arg, x.get_allocator(), std::forward<Rng>(x.get()));
+ }
+
+ void resume()
+ {
+ m_parent_or_leaf.resume();
+ }
+
+ // Disable use of co_await within this coroutine.
+ void await_transform() = delete;
+};
+
+} // namespace nihil
diff --git a/nihil.generator/manual_lifetime.ccm b/nihil.generator/manual_lifetime.ccm
index d249e99..963e6c9 100644
--- a/nihil.generator/manual_lifetime.ccm
+++ b/nihil.generator/manual_lifetime.ccm
@@ -68,8 +68,8 @@ private:
template <typename T>
class manual_lifetime<T &> {
- manual_lifetime() noexcept {}
- ~manual_lifetime() {}
+ manual_lifetime() noexcept = default;
+ ~manual_lifetime() = default;
auto construct(this manual_lifetime &self, T &value) noexcept -> T &
{
@@ -92,8 +92,8 @@ private:
template <typename T>
class manual_lifetime<T &&> {
- manual_lifetime() noexcept {}
- ~manual_lifetime() {}
+ manual_lifetime() noexcept = default;
+ ~manual_lifetime() = default;
auto construct(this manual_lifetime &self, T &&value) noexcept -> T &&
{
diff --git a/nihil.generator/nihil.generator.ccm b/nihil.generator/nihil.generator.ccm
index 9eec5b4..ac9076b 100644
--- a/nihil.generator/nihil.generator.ccm
+++ b/nihil.generator/nihil.generator.ccm
@@ -17,16 +17,19 @@ module;
export module nihil.generator;
+export import :byte_allocator;
+export import :coroutine_traits;
export import :elements_of;
+export import :forward;
export import :generator;
+export import :generator_promise_base;
export import :manual_lifetime;
export import :promise_base_alloc;
export import :util;
-export namespace std::ranges {
+export namespace std::ranges { // NOLINT
template <typename T, typename U, typename Alloc>
constexpr inline bool enable_view<nihil::generator<T, U, Alloc>> = true;
} // namespace std::ranges
-