diff options
25 files changed, 305 insertions, 1076 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index af2bb8c..b51f7b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,6 @@ endif() add_subdirectory(nihil.std) add_subdirectory(nihil.cli) add_subdirectory(nihil.core) -add_subdirectory(nihil.generator) add_subdirectory(nihil.posix) if(NIHIL_UCL) @@ -11,9 +11,8 @@ license all of nihil is in the public domain, with the exception of: -- nihil/generator.ccm (BSL) -- nihil/monad.ccm (MIT) -- nihil/uuid.ccm (MIT) +- nihil.core/monad.ccm (MIT) +- nihil.core/uuid.ccm (MIT) requirements ------------ diff --git a/nihil.cli/CMakeLists.txt b/nihil.cli/CMakeLists.txt index e87d3d4..8f4d1ea 100644 --- a/nihil.cli/CMakeLists.txt +++ b/nihil.cli/CMakeLists.txt @@ -5,7 +5,6 @@ add_library(nihil.cli STATIC) target_link_libraries(nihil.cli PRIVATE nihil.std nihil.core - nihil.generator nihil.posix ) diff --git a/nihil.cli/command_tree.ccm b/nihil.cli/command_tree.ccm index 844b2cf..4c341b7 100644 --- a/nihil.cli/command_tree.ccm +++ b/nihil.cli/command_tree.ccm @@ -2,7 +2,6 @@ export module nihil.cli:command_tree; import nihil.std; -import nihil.generator; import nihil.core; import :command; @@ -103,7 +102,8 @@ struct command_tree_node final { for (auto &&node : self.m_children | std::views::values) { co_yield node; - co_yield elements_of(node.all_children()); + for (auto &&child : node.all_children()) + co_yield child; } } diff --git a/nihil.config/CMakeLists.txt b/nihil.config/CMakeLists.txt index 0d8ffee..95b3fdb 100644 --- a/nihil.config/CMakeLists.txt +++ b/nihil.config/CMakeLists.txt @@ -1,12 +1,14 @@ # This source code is released into the public domain. add_library(nihil.config STATIC) + target_link_libraries(nihil.config PRIVATE nihil.std - nihil.generator + nihil.core nihil.posix nihil.ucl ) + target_sources(nihil.config PUBLIC FILE_SET modules TYPE CXX_MODULES FILES nihil.config.ccm @@ -28,6 +30,8 @@ if(NIHIL_TESTS) ) target_link_libraries(nihil.config.test PRIVATE + nihil.std + nihil.core nihil.config Catch2::Catch2WithMain) diff --git a/nihil.config/store.cc b/nihil.config/store.cc index 08850c4..06c2035 100644 --- a/nihil.config/store.cc +++ b/nihil.config/store.cc @@ -3,7 +3,6 @@ module nihil.config; import nihil.std; import nihil.core; -import nihil.generator; namespace nihil::config { diff --git a/nihil.config/store.ccm b/nihil.config/store.ccm index 2ceb52b..45de35e 100644 --- a/nihil.config/store.ccm +++ b/nihil.config/store.ccm @@ -4,7 +4,6 @@ export module nihil.config:store; // The configuration store. There should only be one of these. import nihil.std; -import nihil.generator; import nihil.core; namespace nihil::config { diff --git a/nihil.core/CMakeLists.txt b/nihil.core/CMakeLists.txt index e9998a3..d0713ce 100644 --- a/nihil.core/CMakeLists.txt +++ b/nihil.core/CMakeLists.txt @@ -14,6 +14,7 @@ target_sources(nihil.core error.ccm features.ccm flagset.ccm + generator.ccm guard.ccm match.ccm monad.ccm @@ -25,3 +26,31 @@ target_sources(nihil.core tabulate.ccm uuid.ccm ) + +if (NIHIL_TESTS) + add_executable(nihil.core.test + capture_stream.test.cc + ctype.test.cc + error.test.cc + flagset.test.cc + generator.test.cc + guard.test.cc + match.test.cc + monad.test.cc + next_word.test.cc + parse_size.test.cc + skipws.test.cc + tabulate.test.cc + ) + + target_link_libraries(nihil.core.test PRIVATE + nihil.std + nihil.core + Catch2::Catch2WithMain) + + include(CTest) + include(Catch) + catch_discover_tests(nihil.core.test) + + enable_testing() +endif () diff --git a/nihil.core/error.test.cc b/nihil.core/error.test.cc index f4ec1ee..7a923e4 100644 --- a/nihil.core/error.test.cc +++ b/nihil.core/error.test.cc @@ -4,7 +4,6 @@ import nihil.std; import nihil.core; -import nihil.util; namespace { inline constexpr auto *test_tags = "[nihil][nihil.error]"; 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; diff --git a/nihil.generator/generator.test.cc b/nihil.core/generator.test.cc index 1782dfb..1fc0f22 100644 --- a/nihil.generator/generator.test.cc +++ b/nihil.core/generator.test.cc @@ -3,7 +3,7 @@ #include <catch2/catch_test_macros.hpp> import nihil.std; -import nihil.generator; +import nihil.core; namespace { inline auto constexpr test_tags = "[nihil][nihil.generator]"; @@ -58,6 +58,21 @@ SCENARIO("A generator that yields pointers", test_tags) } } +SCENARIO("A generator that yields lvalues", test_tags) +{ + GIVEN ("A generator that yields pointers") { + auto one = 1, two = 2; + auto fn = [&]() -> nihil::generator<int> { + co_yield one; + co_yield two; + }; + + THEN ("The pointers point to the original values") { + REQUIRE(std::ranges::equal(fn(), std::vector{1, 2})); + } + } +} + TEST_CASE("generator: exceptions", "[generator]") { auto fn = []() -> nihil::generator<int> { @@ -71,6 +86,8 @@ TEST_CASE("generator: exceptions", "[generator]") REQUIRE_THROWS_AS(it++, std::runtime_error); } +#if 0 +// TODO: Re-enable this test once we have a standard-compliant generator. TEST_CASE("generator: elements_of", "[generator]") { auto fn1 = [] -> nihil::generator<int> { @@ -88,4 +105,5 @@ TEST_CASE("generator: elements_of", "[generator]") REQUIRE(values == std::vector{1, 2, 3}); } +#endif } // anonymous namespace diff --git a/nihil.core/nihil.core.ccm b/nihil.core/nihil.core.ccm index 37ad032..7634026 100644 --- a/nihil.core/nihil.core.ccm +++ b/nihil.core/nihil.core.ccm @@ -8,6 +8,7 @@ export import :errc; export import :error; export import :features; export import :flagset; +export import :generator; export import :guard; export import :match; export import :monad; diff --git a/nihil.generator/CMakeLists.txt b/nihil.generator/CMakeLists.txt deleted file mode 100644 index d9eb854..0000000 --- a/nihil.generator/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -# This source code is released into the public domain. - -add_library(nihil.generator STATIC) -target_link_libraries(nihil.generator PRIVATE nihil.std) -target_sources(nihil.generator - PUBLIC FILE_SET modules TYPE CXX_MODULES FILES - 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 -) - -if(NIHIL_TESTS) - enable_testing() - - add_executable(nihil.generator.test generator.test.cc) - target_link_libraries(nihil.generator.test PRIVATE - nihil.generator - Catch2::Catch2WithMain - ) - - include(CTest) - include(Catch) - catch_discover_tests(nihil.generator.test) -endif() diff --git a/nihil.generator/byte_allocator.ccm b/nihil.generator/byte_allocator.ccm deleted file mode 100644 index 86b2edf..0000000 --- a/nihil.generator/byte_allocator.ccm +++ /dev/null @@ -1,24 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:byte_allocator; - -import nihil.std; - -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 deleted file mode 100644 index fde4393..0000000 --- a/nihil.generator/coroutine_traits.ccm +++ /dev/null @@ -1,66 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:coroutine_traits; - -import nihil.std; -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/elements_of.ccm b/nihil.generator/elements_of.ccm deleted file mode 100644 index 74c8b76..0000000 --- a/nihil.generator/elements_of.ccm +++ /dev/null @@ -1,66 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:elements_of; - -import nihil.std; -import :util; - -namespace nihil { - -export template <typename Range, typename Allocator = use_allocator_arg> -struct elements_of { - explicit constexpr elements_of(Range &&range) noexcept - requires std::is_default_constructible_v<Allocator> - : m_range(static_cast<Range &&>(range)) - { - } - - constexpr elements_of(Range &&range, Allocator &&alloc) noexcept - : m_range(static_cast<Range &&>(range)) - , m_alloc(static_cast<Allocator &&>(alloc)) - {} - - constexpr elements_of(elements_of &&) noexcept = default; - - constexpr elements_of(const elements_of &) = delete; - - constexpr auto operator=(this elements_of &, const elements_of &) - -> elements_of & = delete; - constexpr auto operator=(this elements_of &, elements_of &&) noexcept - -> elements_of & = delete; - - [[nodiscard]] constexpr auto - get(this elements_of const &self) noexcept -> Range && - { - return static_cast<Range &&>(self.m_range); - } - - [[nodiscard]] constexpr auto - get_allocator(this elements_of const &self) noexcept -> Allocator - { - return self.m_alloc; - } - -private: - [[no_unique_address]] Allocator m_alloc; - Range &&m_range; -}; - -export template <typename Range> -elements_of(Range &&) -> elements_of<Range>; - -export template <typename Range, typename Allocator> -elements_of(Range &&, Allocator &&) -> elements_of<Range, Allocator>; - -} // namespace nihil diff --git a/nihil.generator/forward.ccm b/nihil.generator/forward.ccm deleted file mode 100644 index 2aa2448..0000000 --- a/nihil.generator/forward.ccm +++ /dev/null @@ -1,26 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:forward; - -import nihil.std; -import :util; - -namespace nihil { - -export template <typename Ref, - typename Value = std::remove_cvref_t<Ref>, - typename Allocator = use_allocator_arg> -struct generator; - -} // namespace nihil diff --git a/nihil.generator/generator.ccm b/nihil.generator/generator.ccm deleted file mode 100644 index a2fcafb..0000000 --- a/nihil.generator/generator.ccm +++ /dev/null @@ -1,323 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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 <cassert> - -export module nihil.generator:generator; - -import nihil.std; -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 { - -// TODO : make layout compatible promise casts possible -export template <typename Ref, typename Value, typename Alloc> -struct 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 - : 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 {}; - } - -private: - explicit generator(coroutine_handle coro) noexcept - : m_coro(coro) - { - } - -public: // to get around access restrictions for __yield_sequence_awaitable - 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 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: - 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 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 - auto get_coro() noexcept -> std::coroutine_handle<> - { - return m_coro; - } - - auto get_promise() noexcept -> promise_base * - { - return m_promise; - } - -private: - promise_base *m_promise; - std::coroutine_handle<> m_coro; - bool m_started = false; -}; - -} // namespace nihil diff --git a/nihil.generator/generator_promise.ccm b/nihil.generator/generator_promise.ccm deleted file mode 100644 index 3e8aa8c..0000000 --- a/nihil.generator/generator_promise.ccm +++ /dev/null @@ -1,61 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:generator_promise; - -import nihil.std; -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 deleted file mode 100644 index 6b11b40..0000000 --- a/nihil.generator/generator_promise_base.ccm +++ /dev/null @@ -1,200 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:generator_promise_base; - -import nihil.std; -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 struct 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 ¤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 <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 deleted file mode 100644 index 44bc0a8..0000000 --- a/nihil.generator/manual_lifetime.ccm +++ /dev/null @@ -1,114 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:manual_lifetime; - -import nihil.std; - -namespace nihil { - -template <typename T> -struct manual_lifetime { - manual_lifetime() noexcept {} - ~manual_lifetime() {} - - template <typename ...Args> - auto construct(this manual_lifetime &self, Args && ...args) - noexcept(std::is_nothrow_constructible_v<T, Args...>) - -> T & - { - return *::new (static_cast<void*>(std::addressof(self.m_value))) - T(static_cast<Args &&>(args)...); - } - - void destruct(this manual_lifetime &self) - noexcept(std::is_nothrow_destructible_v<T>) - { - self.m_value.~T(); - } - - auto get(this manual_lifetime &self) noexcept -> T & - { - return self.m_value; - } - - auto get(this manual_lifetime &&self) noexcept -> T && - { - return static_cast<T&&>(self.m_value); - } - - auto get(this manual_lifetime const &self) noexcept -> T const & - { - return self.m_value; - } - - auto get(this manual_lifetime const &&self) noexcept -> T const && - { - return static_cast<T const &&>(self.m_value); - } - -private: - union { - std::remove_const_t<T> m_value; - }; -}; - -template <typename T> -struct manual_lifetime<T &> { - manual_lifetime() noexcept = default; - ~manual_lifetime() = default; - - auto construct(this manual_lifetime &self, T &value) noexcept -> T & - { - self.m_value = std::addressof(value); - return value; - } - - auto destruct(this manual_lifetime &) noexcept -> void - { - } - - auto get(this manual_lifetime const &self) noexcept -> T & - { - return *self.m_value; - } - -private: - T *m_value = nullptr; -}; - -template <typename T> -struct manual_lifetime<T &&> { - manual_lifetime() noexcept = default; - ~manual_lifetime() = default; - - auto construct(this manual_lifetime &self, T &&value) noexcept -> T && - { - self.m_value = std::addressof(value); - return static_cast<T &&>(value); - } - - void destruct(this manual_lifetime &) noexcept - { - } - - auto get(this manual_lifetime const &self) noexcept -> T && - { - return static_cast<T &&>(*self.m_value); - } - -private: - T* m_value = nullptr; -}; - -} diff --git a/nihil.generator/nihil.generator.ccm b/nihil.generator/nihil.generator.ccm deleted file mode 100644 index fc6a097..0000000 --- a/nihil.generator/nihil.generator.ccm +++ /dev/null @@ -1,27 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator; - -import nihil.std; - -export import :coroutine_traits; -export import :elements_of; -export import :generator; - -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 diff --git a/nihil.generator/promise_base_alloc.ccm b/nihil.generator/promise_base_alloc.ccm deleted file mode 100644 index 7fd544b..0000000 --- a/nihil.generator/promise_base_alloc.ccm +++ /dev/null @@ -1,90 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:promise_base_alloc; - -import nihil.std; -import :util; - -namespace nihil { - -template<typename Alloc> -struct promise_base_alloc -{ - template<typename... Args> - static void* operator new(std::size_t frame_size, std::allocator_arg_t, Alloc alloc, Args &...) - { - void* frame = alloc.allocate(padded_frame_size(frame_size)); - - // Store allocator at end of the coroutine frame. Assuming the - // allocator's move constructor is non-throwing (a requirement - // for allocators) - auto *alloc_address = static_cast<void*>(std::addressof(get_allocator(frame, frame_size))); - ::new (alloc_address) Alloc(std::move(alloc)); - - return frame; - } - - template<typename This, typename... Args> - static void* operator new(std::size_t frame_size, This &, std::allocator_arg_t, Alloc alloc, Args &...) - { - return promise_base_alloc::operator new(frame_size, std::allocator_arg, std::move(alloc)); - } - - static void operator delete(void* ptr, std::size_t frame_size) noexcept - { - auto &alloc = get_allocator(ptr, frame_size); - auto local_alloc = Alloc(std::move(alloc)); - - alloc.~Alloc(); - - local_alloc.deallocate(static_cast<std::byte*>(ptr), padded_frame_size(frame_size)); - } - -private: - [[nodiscard]] static constexpr auto offset_of_allocator(std::size_t frame_size) noexcept -> std::size_t - { - return aligned_allocation_size(frame_size, alignof(Alloc)); - } - - [[nodiscard]] static constexpr auto padded_frame_size(std::size_t frame_size) noexcept -> std::size_t - { - return offset_of_allocator(frame_size) + sizeof(Alloc); - } - - [[nodiscard]] static auto get_allocator(void* frame, std::size_t frame_size) noexcept -> Alloc & - { - return *reinterpret_cast<Alloc*>( - static_cast<char*>(frame) + offset_of_allocator(frame_size)); - } - -}; - -template<typename Alloc> -requires (!allocator_needs_to_be_stored<Alloc>) -struct promise_base_alloc<Alloc> -{ - static void* operator new(std::size_t size) - { - auto alloc = Alloc(); - return alloc.allocate(size); - } - - static void operator delete(void *ptr, std::size_t size) noexcept - { - auto alloc = Alloc(); - alloc.deallocate(static_cast<std::byte *>(ptr), size); - } -}; - -} // namespace nihil diff --git a/nihil.generator/util.ccm b/nihil.generator/util.ccm deleted file mode 100644 index 259499a..0000000 --- a/nihil.generator/util.ccm +++ /dev/null @@ -1,34 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// 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) -/////////////////////////////////////////////////////////////////////////////// - -export module nihil.generator:util; - -import nihil.std; - -namespace nihil { - -export struct use_allocator_arg {}; - -template <typename Alloc> -constexpr bool allocator_needs_to_be_stored = - !std::allocator_traits<Alloc>::is_always_equal::value || - !std::is_default_constructible_v<Alloc>; - -// Round s up to next multiple of a. -[[nodiscard]] constexpr auto -aligned_allocation_size(std::size_t s, std::size_t a) -> std::size_t -{ - return (s + a - 1) & ~(a - 1); -} - -} // namespace nihil diff --git a/nihil.std/nihil.std.ccm b/nihil.std/nihil.std.ccm index 349ab95..cb6a46d 100644 --- a/nihil.std/nihil.std.ccm +++ b/nihil.std/nihil.std.ccm @@ -421,6 +421,7 @@ using std::system_error; // <type_traits> using std::add_pointer_t; +using std::conditional_t; using std::false_type; using std::invoke_result; using std::is_convertible; @@ -437,10 +438,14 @@ using std::is_move_assignable; using std::is_move_assignable_v; using std::is_nothrow_constructible; using std::is_nothrow_constructible_v; +using std::is_nothrow_copy_constructible; +using std::is_nothrow_copy_constructible_v; using std::is_nothrow_destructible; using std::is_nothrow_destructible_v; using std::is_nothrow_move_constructible; using std::is_nothrow_move_constructible_v; +using std::is_pointer; +using std::is_pointer_v; using std::is_reference; using std::is_reference_v; using std::is_same; @@ -448,6 +453,7 @@ using std::is_same_v; using std::remove_const_t; using std::remove_cv_t; using std::remove_cvref_t; +using std::remove_reference_t; using std::true_type; // <unordered_set> @@ -463,6 +469,7 @@ using std::move; using std::pair; // <variant> +using std::get; using std::get_if; using std::monostate; using std::variant; |
