aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.generator
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-29 17:16:22 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-29 17:16:22 +0100
commit4fa6821e0645ff61a9380cd090abff472205c630 (patch)
treebd95f13b2dc0bd9692681f50c365d2914a520bfe /nihil.generator
parente5180acf5f2dfac788e8c12886095ed1ac66fae5 (diff)
downloadnihil-4fa6821e0645ff61a9380cd090abff472205c630.tar.gz
nihil-4fa6821e0645ff61a9380cd090abff472205c630.tar.bz2
add clang-tidy support
Diffstat (limited to 'nihil.generator')
-rw-r--r--nihil.generator/CMakeLists.txt6
-rw-r--r--nihil.generator/elements_of.ccm69
-rw-r--r--nihil.generator/generator.ccm209
-rw-r--r--nihil.generator/manual_lifetime.ccm117
-rw-r--r--nihil.generator/nihil.generator.ccm32
-rw-r--r--nihil.generator/promise_base_alloc.ccm94
-rw-r--r--nihil.generator/test.cc2
-rw-r--r--nihil.generator/util.ccm37
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