aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.core/generator.ccm
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-07-02 05:49:47 +0100
committerLexi Winter <lexi@le-fay.org>2025-07-02 05:49:47 +0100
commitebe4cb0bdeabd06a31072547af47cacaab7f78c0 (patch)
tree65a81c2c86260b595107ee6c5505583f9afaf39d /nihil.core/generator.ccm
parent5adeb648f74c1771164c0686d6e0fc584cf36d9e (diff)
downloadnihil-ebe4cb0bdeabd06a31072547af47cacaab7f78c0.tar.gz
nihil-ebe4cb0bdeabd06a31072547af47cacaab7f78c0.tar.bz2
replace nihil::generator
the new implementation is much simpler and PD-licensed. the only downside is it doesn't support elements_of. while here, move it to nihil.core.
Diffstat (limited to 'nihil.core/generator.ccm')
-rw-r--r--nihil.core/generator.ccm240
1 files changed, 240 insertions, 0 deletions
diff --git a/nihil.core/generator.ccm b/nihil.core/generator.ccm
new file mode 100644
index 0000000..4d2bfe3
--- /dev/null
+++ b/nihil.core/generator.ccm
@@ -0,0 +1,240 @@
+// generator - Single-header, ranges-compatible generator type built
+// on C++20 coroutines
+// Written in 2021 by Sy Brand (tartanllama@gmail.com, @TartanLlama)
+//
+// Documentation available at https://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+export module nihil.core:generator;
+
+import nihil.std;
+
+namespace nihil {
+
+export template <typename T>
+struct generator
+{
+private:
+ struct promise
+ {
+ using value_type = std::remove_reference_t<T>;
+ using reference_type =
+ std::conditional_t<std::is_pointer_v<value_type>, value_type, value_type &>;
+ using pointer_type =
+ std::conditional_t<std::is_pointer_v<value_type>, value_type, value_type *>;
+
+ promise() = default;
+
+ [[nodiscard]] auto get_return_object() -> generator
+ {
+ return generator(std::coroutine_handle<promise>::from_promise(*this));
+ }
+
+ [[nodiscard]] auto initial_suspend() const -> std::suspend_always
+ {
+ return {};
+ }
+
+ [[nodiscard]] auto final_suspend() const noexcept -> std::suspend_always
+ {
+ return {};
+ }
+
+ void return_void() const noexcept
+ {
+ }
+
+ void unhandled_exception() noexcept
+ {
+ exception_ = std::current_exception();
+ }
+
+ void rethrow_if_exception()
+ {
+ if (exception_) {
+ std::rethrow_exception(exception_);
+ }
+ }
+
+ std::suspend_always yield_value(value_type &&v) noexcept
+ {
+ value_ = std::move(v);
+ return {};
+ }
+
+ std::suspend_always yield_value(value_type const &v) noexcept
+ requires(!std::is_reference_v<T>)
+ {
+ value_ = v;
+ return {};
+ }
+
+ std::suspend_always yield_value(value_type &v) noexcept
+ requires(std::is_reference_v<T>)
+ {
+ value_ = &v;
+ return {};
+ }
+
+ std::exception_ptr exception_;
+ std::variant<std::monostate, value_type, value_type *> value_;
+ };
+
+public:
+ using promise_type = promise;
+ class sentinel
+ {
+ };
+
+ class iterator
+ {
+ using handle_type = std::coroutine_handle<promise_type>;
+
+ public:
+ using value_type = typename promise_type::value_type;
+ using reference_type = typename promise_type::reference_type;
+ using pointer_type = typename promise_type::pointer_type;
+ using difference_type = std::ptrdiff_t;
+
+ iterator() = default;
+ ~iterator()
+ {
+ if (handle_)
+ handle_.destroy();
+ }
+
+ // Non-copyable because coroutine handles point to a unique resource
+ iterator(iterator const &) = delete;
+ iterator(iterator &&rhs) noexcept
+ : handle_(std::exchange(rhs.handle_, nullptr))
+ {
+ }
+
+ auto operator=(iterator const &) -> iterator & = delete;
+
+ auto operator=(iterator &&rhs) noexcept -> iterator &
+ {
+ handle_ = std::exchange(rhs.handle_, nullptr);
+ return *this;
+ }
+
+ friend auto operator==(iterator const &it, sentinel) noexcept -> bool
+ {
+ return (!it.handle_ || it.handle_.done());
+ }
+
+ auto operator++() -> iterator &
+ {
+ handle_.resume();
+ if (handle_.done()) {
+ handle_.promise().rethrow_if_exception();
+ }
+ return *this;
+ }
+
+ void operator++(int)
+ {
+ (void)this->operator++();
+ }
+
+ reference_type operator*() const
+ noexcept(noexcept(std::is_nothrow_copy_constructible_v<reference_type>))
+ requires(std::is_reference_v<T>)
+ {
+ return *std::get<value_type *>(handle_.promise().value_);
+ }
+
+ reference_type operator*() const
+ noexcept(noexcept(std::is_nothrow_copy_constructible_v<reference_type>))
+ requires(!std::is_reference_v<T>)
+ {
+ return std::get<value_type>(handle_.promise().value_);
+ }
+
+ value_type *operator->() const
+ noexcept(noexcept(std::is_nothrow_copy_constructible_v<reference_type>))
+ requires(std::is_reference_v<T>)
+ {
+ return std::get<value_type *>(handle_.promise().value_);
+ }
+
+ value_type *operator->() const
+ noexcept(noexcept(std::is_nothrow_copy_constructible_v<reference_type>))
+ requires(!std::is_reference_v<T>)
+ {
+ return &std::get<value_type>(handle_.promise().value_);
+ }
+
+ private:
+ friend struct generator;
+
+ explicit iterator(handle_type handle)
+ : handle_(handle)
+ {
+ }
+
+ handle_type handle_;
+ };
+
+ using handle_type = std::coroutine_handle<promise_type>;
+
+ generator() noexcept = default;
+ ~generator()
+ {
+ if (handle_)
+ handle_.destroy();
+ }
+
+ generator(generator const &) = delete;
+
+ generator(generator &&rhs) noexcept
+ : handle_(std::exchange(rhs.handle_, nullptr))
+ {
+ }
+
+ auto operator=(generator const &) -> generator & = delete;
+
+ auto operator=(generator &&rhs) noexcept -> generator &
+ {
+ swap(rhs);
+ return *this;
+ }
+
+ auto begin() -> iterator
+ {
+ handle_.resume();
+ if (handle_.done()) {
+ handle_.promise().rethrow_if_exception();
+ }
+ return iterator(std::exchange(handle_, nullptr));
+ }
+
+ auto end() const noexcept -> sentinel
+ {
+ return {};
+ }
+
+ void swap(generator &other) noexcept
+ {
+ std::swap(handle_, other.handle_);
+ }
+
+private:
+ friend class iterator;
+ explicit generator(handle_type handle) noexcept
+ : handle_(handle)
+ {
+ }
+
+ handle_type handle_ = nullptr;
+};
+} // namespace nihil
+
+export template <typename T>
+inline constexpr bool std::ranges::enable_view<nihil::generator<T>> = true;