diff options
Diffstat (limited to 'nihil.generator')
| -rw-r--r-- | nihil.generator/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | nihil.generator/elements_of.ccm | 69 | ||||
| -rw-r--r-- | nihil.generator/generator.ccm | 209 | ||||
| -rw-r--r-- | nihil.generator/manual_lifetime.ccm | 117 | ||||
| -rw-r--r-- | nihil.generator/nihil.generator.ccm | 32 | ||||
| -rw-r--r-- | nihil.generator/promise_base_alloc.ccm | 94 | ||||
| -rw-r--r-- | nihil.generator/test.cc | 2 | ||||
| -rw-r--r-- | nihil.generator/util.ccm | 37 |
8 files changed, 368 insertions, 198 deletions
diff --git a/nihil.generator/CMakeLists.txt b/nihil.generator/CMakeLists.txt index 7d278a8..56afdac 100644 --- a/nihil.generator/CMakeLists.txt +++ b/nihil.generator/CMakeLists.txt @@ -3,7 +3,13 @@ add_library(nihil.generator STATIC) target_sources(nihil.generator PUBLIC FILE_SET modules TYPE CXX_MODULES FILES + nihil.generator.ccm + generator.ccm + elements_of.ccm + manual_lifetime.ccm + promise_base_alloc.ccm + util.ccm ) if(NIHIL_TESTS) diff --git a/nihil.generator/elements_of.ccm b/nihil.generator/elements_of.ccm new file mode 100644 index 0000000..0e34eb9 --- /dev/null +++ b/nihil.generator/elements_of.ccm @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 <concepts> + +export module nihil.generator:elements_of; + +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/generator.ccm b/nihil.generator/generator.ccm index f022287..27e8103 100644 --- a/nihil.generator/generator.ccm +++ b/nihil.generator/generator.ccm @@ -21,197 +21,20 @@ module; #include <type_traits> #include <utility> -export module nihil.generator; +export module nihil.generator:generator; -namespace nihil { - -template <typename _T> -class __manual_lifetime { - public: - __manual_lifetime() noexcept {} - ~__manual_lifetime() {} - - template <typename... _Args> - _T& construct(_Args&&... __args) noexcept(std::is_nothrow_constructible_v<_T, _Args...>) { - return *::new (static_cast<void*>(std::addressof(__value_))) _T((_Args&&)__args...); - } - - void destruct() noexcept(std::is_nothrow_destructible_v<_T>) { - __value_.~_T(); - } - - _T& get() & noexcept { - return __value_; - } - _T&& get() && noexcept { - return static_cast<_T&&>(__value_); - } - const _T& get() const & noexcept { - return __value_; - } - const _T&& get() const && noexcept { - return static_cast<const _T&&>(__value_); - } - - private: - union { - std::remove_const_t<_T> __value_; - }; -}; - -template <typename _T> -class __manual_lifetime<_T&> { - public: - __manual_lifetime() noexcept : __value_(nullptr) {} - ~__manual_lifetime() {} - - _T& construct(_T& __value) noexcept { - __value_ = std::addressof(__value); - return __value; - } - - void destruct() noexcept {} - - _T& get() const noexcept { - return *__value_; - } - - private: - _T* __value_; -}; - -template <typename _T> -class __manual_lifetime<_T&&> { - public: - __manual_lifetime() noexcept : __value_(nullptr) {} - ~__manual_lifetime() {} - - _T&& construct(_T&& __value) noexcept { - __value_ = std::addressof(__value); - return static_cast<_T&&>(__value); - } - - void destruct() noexcept {} - - _T&& get() const noexcept { - return static_cast<_T&&>(*__value_); - } - - private: - _T* __value_; -}; - -struct use_allocator_arg {}; - -namespace ranges { - -export template <typename _Rng, typename _Allocator = use_allocator_arg> -struct elements_of { - explicit constexpr elements_of(_Rng&& __rng) noexcept - requires std::is_default_constructible_v<_Allocator> - : __range(static_cast<_Rng&&>(__rng)) - {} - - constexpr elements_of(_Rng&& __rng, _Allocator&& __alloc) noexcept - : __range((_Rng&&)__rng), __alloc((_Allocator&&)__alloc) {} - - constexpr elements_of(elements_of&&) noexcept = default; - - constexpr elements_of(const elements_of &) = delete; - constexpr elements_of &operator=(const elements_of &) = delete; - constexpr elements_of &operator=(elements_of &&) = delete; - - constexpr _Rng&& get() noexcept { - return static_cast<_Rng&&>(__range); - } - - constexpr _Allocator get_allocator() const noexcept { - return __alloc; - } - -private: - [[no_unique_address]] _Allocator __alloc; // \expos - _Rng && __range; // \expos -}; - -export template <typename _Rng> -elements_of(_Rng &&) -> elements_of<_Rng>; - -export template <typename _Rng, typename Allocator> -elements_of(_Rng &&, Allocator&&) -> elements_of<_Rng, Allocator>; - -} // namespace ranges - -template <typename _Alloc> -static 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. -constexpr size_t __aligned_allocation_size(size_t s, size_t a) { - return (s + a - 1) & ~(a - 1); -} +import :elements_of; +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 _Alloc> -class __promise_base_alloc { - static constexpr std::size_t __offset_of_allocator(std::size_t __frameSize) noexcept { - return __aligned_allocation_size(__frameSize, alignof(_Alloc)); - } - - static constexpr std::size_t __padded_frame_size(std::size_t __frameSize) noexcept { - return __offset_of_allocator(__frameSize) + sizeof(_Alloc); - } - - static _Alloc& __get_allocator(void* __frame, std::size_t __frameSize) noexcept { - return *reinterpret_cast<_Alloc*>( - static_cast<char*>(__frame) + __offset_of_allocator(__frameSize)); - } - -public: - template<typename... _Args> - static void* operator new(std::size_t __frameSize, std::allocator_arg_t, _Alloc __alloc, _Args&...) { - void* __frame = __alloc.allocate(__padded_frame_size(__frameSize)); - - // Store allocator at end of the coroutine frame. - // Assuming the allocator's move constructor is non-throwing (a requirement for allocators) - ::new (static_cast<void*>(std::addressof(__get_allocator(__frame, __frameSize)))) _Alloc(std::move(__alloc)); - - return __frame; - } - - template<typename _This, typename... _Args> - static void* operator new(std::size_t __frameSize, _This&, std::allocator_arg_t, _Alloc __alloc, _Args&...) { - return __promise_base_alloc::operator new(__frameSize, std::allocator_arg, std::move(__alloc)); - } - - static void operator delete(void* __ptr, std::size_t __frameSize) noexcept { - _Alloc& __alloc = __get_allocator(__ptr, __frameSize); - _Alloc __localAlloc(std::move(__alloc)); - __alloc.~Alloc(); - __localAlloc.deallocate(static_cast<std::byte*>(__ptr), __padded_frame_size(__frameSize)); - } -}; - -template<typename _Alloc> - requires (!__allocator_needs_to_be_stored<_Alloc>) -class __promise_base_alloc<_Alloc> { -public: - static void* operator new(std::size_t __size) { - _Alloc __alloc; - return __alloc.allocate(__size); - } - - static void operator delete(void* __ptr, std::size_t __size) noexcept { - _Alloc __alloc; - __alloc.deallocate(static_cast<std::byte*>(__ptr), __size); - } -}; template<typename _Ref> struct __generator_promise_base @@ -226,8 +49,8 @@ struct __generator_promise_base // 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_; + manual_lifetime<std::exception_ptr> __exception_; + manual_lifetime<_Ref> __value_; explicit __generator_promise_base(std::coroutine_handle<> thisCoro) noexcept : __root_(this) @@ -345,13 +168,13 @@ struct __generator_promise_base template <typename _OValue, typename _OAlloc> __yield_sequence_awaiter<generator<_Ref, _OValue, _OAlloc>> - yield_value(nihil::ranges::elements_of<generator<_Ref, _OValue, _OAlloc>> __g) noexcept { + 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::ranges::elements_of<_Rng, _Allocator> && __x) { + 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); @@ -372,7 +195,7 @@ 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> { + , public promise_base_alloc<_ByteAllocator> { __generator_promise() noexcept : __generator_promise_base<_Ref>(std::coroutine_handle<__generator_promise>::from_promise(*this)) {} @@ -387,7 +210,7 @@ struct __generator_promise<generator<_Ref, _Value, _Alloc>, _ByteAllocator, _Exp template <std::ranges::range _Rng> typename __generator_promise_base<_Ref>::template __yield_sequence_awaiter<generator<_Ref, _Value, _Alloc>> - yield_value(nihil::ranges::elements_of<_Rng> && __x) { + 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"); @@ -682,11 +505,3 @@ private: }; } // namespace nihil - -export namespace std::ranges { - -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/manual_lifetime.ccm b/nihil.generator/manual_lifetime.ccm new file mode 100644 index 0000000..d249e99 --- /dev/null +++ b/nihil.generator/manual_lifetime.ccm @@ -0,0 +1,117 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 <concepts> +#include <memory> + +export module nihil.generator:manual_lifetime; + +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> +class manual_lifetime<T &> { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + auto construct(this manual_lifetime &self, T &value) noexcept -> T & + { + self.m_value = std::addressof(value); + return self.m_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> +class manual_lifetime<T &&> { + manual_lifetime() noexcept {} + ~manual_lifetime() {} + + 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 new file mode 100644 index 0000000..9eec5b4 --- /dev/null +++ b/nihil.generator/nihil.generator.ccm @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 <ranges> + +export module nihil.generator; + +export import :elements_of; +export import :generator; +export import :manual_lifetime; +export import :promise_base_alloc; +export import :util; + +export namespace std::ranges { + +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 new file mode 100644 index 0000000..e59fc57 --- /dev/null +++ b/nihil.generator/promise_base_alloc.ccm @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 <cstdlib> +#include <memory> + +export module nihil.generator:promise_base_alloc; + +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/test.cc b/nihil.generator/test.cc index d167f30..49272b4 100644 --- a/nihil.generator/test.cc +++ b/nihil.generator/test.cc @@ -46,7 +46,7 @@ TEST_CASE("generator: elements_of", "[generator]") }; auto fn2 = [&fn1] -> nihil::generator<int> { - co_yield nihil::ranges::elements_of(fn1()); + co_yield nihil::elements_of(fn1()); }; auto values = std::vector<int>(); diff --git a/nihil.generator/util.ccm b/nihil.generator/util.ccm new file mode 100644 index 0000000..4d732b9 --- /dev/null +++ b/nihil.generator/util.ccm @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////// +// 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 <concepts> +#include <memory> + +export module nihil.generator:util; + +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 |
