// 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 // . export module nihil.core:generator; import nihil.std; namespace nihil { export template struct generator { private: struct promise { using value_type = std::remove_reference_t; using reference_type = std::conditional_t, value_type, value_type &>; using pointer_type = std::conditional_t, value_type, value_type *>; promise() = default; [[nodiscard]] auto get_return_object() -> generator { return generator(std::coroutine_handle::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) { value_ = v; return {}; } std::suspend_always yield_value(value_type &v) noexcept requires(std::is_reference_v) { value_ = &v; return {}; } std::exception_ptr exception_; std::variant value_; }; public: using promise_type = promise; class sentinel { }; class iterator { using handle_type = std::coroutine_handle; 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)) requires(std::is_reference_v) { return *std::get(handle_.promise().value_); } reference_type operator*() const noexcept(noexcept(std::is_nothrow_copy_constructible_v)) requires(!std::is_reference_v) { return std::get(handle_.promise().value_); } value_type *operator->() const noexcept(noexcept(std::is_nothrow_copy_constructible_v)) requires(std::is_reference_v) { return std::get(handle_.promise().value_); } value_type *operator->() const noexcept(noexcept(std::is_nothrow_copy_constructible_v)) requires(!std::is_reference_v) { return &std::get(handle_.promise().value_); } private: friend struct generator; explicit iterator(handle_type handle) : handle_(handle) { } handle_type handle_; }; using handle_type = std::coroutine_handle; 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 inline constexpr bool std::ranges::enable_view> = true;