From 034cd404a129103a8dd7747e6bd00ffd5550da93 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Mon, 30 Jun 2025 07:51:23 +0100 Subject: refactoring --- nihil.generator/generator_promise_base.ccm | 205 +++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 nihil.generator/generator_promise_base.ccm (limited to 'nihil.generator/generator_promise_base.ccm') 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 +#include +#include + +export module nihil.generator:generator_promise_base; + +import :elements_of; +import :forward; +import :manual_lifetime; + +namespace nihil { + +template +struct generator_promise_base +{ + template + 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 m_exception; + manual_lifetime 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 + auto + await_suspend(std::coroutine_handle 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) + -> std::suspend_always + { + m_root->m_value.construct((Ref &&)x); + return {}; + } + + template + requires(!std::is_reference_v) && std::is_convertible_v + auto + yield_value(T &&x) noexcept(std::is_nothrow_constructible_v) -> std::suspend_always + { + m_root->m_value.construct((T &&)x); + return {}; + } + + template + 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 + auto + await_suspend(std::coroutine_handle h) noexcept -> std::coroutine_handle<> + { + generator_promise_base ¤t = 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 + auto yield_value(nihil::elements_of> g) noexcept + -> yield_sequence_awaiter> + + { + return std::move(g).get(); + } + + template + auto yield_value(nihil::elements_of &&x) + -> yield_sequence_awaiter, Allocator>> + + { + return [](std::allocator_arg_t, Allocator, + auto &&rng) -> generator, Allocator> { + for (auto &&e : rng) + co_yield static_cast(e); + }(std::allocator_arg, x.get_allocator(), std::forward(x.get())); + } + + void resume() + { + m_parent_or_leaf.resume(); + } + + // Disable use of co_await within this coroutine. + void await_transform() = delete; +}; + +} // namespace nihil -- cgit v1.2.3