diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-30 07:51:23 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-30 07:51:23 +0100 |
| commit | 034cd404a129103a8dd7747e6bd00ffd5550da93 (patch) | |
| tree | d27946517d4d9333abd26ac50bbd4a436093e2ce /nihil.generator/generator.ccm | |
| parent | 3e7902f7d790a486d3d9cb978df193f07f3a6ad9 (diff) | |
| download | nihil-034cd404a129103a8dd7747e6bd00ffd5550da93.tar.gz nihil-034cd404a129103a8dd7747e6bd00ffd5550da93.tar.bz2 | |
refactoring
Diffstat (limited to 'nihil.generator/generator.ccm')
| -rw-r--r-- | nihil.generator/generator.ccm | 717 |
1 files changed, 269 insertions, 448 deletions
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 |
