aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-21 12:27:20 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-21 12:27:20 +0100
commit243d958df14b85788232aca623b83826115a5eb9 (patch)
treed1f698296b053359a5563731cda8c51df9ab9a6c /modules
parent8a36eb498e1a1c2cf2e886356faa4ce67e52e874 (diff)
downloadnihil-243d958df14b85788232aca623b83826115a5eb9.tar.gz
nihil-243d958df14b85788232aca623b83826115a5eb9.tar.bz2
rename modules/ to nihil/
Diffstat (limited to 'modules')
-rw-r--r--modules/CMakeLists.txt13
-rw-r--r--modules/ctype.ccm86
-rw-r--r--modules/fd.ccm309
-rw-r--r--modules/generator.ccm691
-rw-r--r--modules/generic_error.ccm26
-rw-r--r--modules/getenv.ccm47
-rw-r--r--modules/guard.ccm50
-rw-r--r--modules/nihil.ccm15
-rw-r--r--modules/tabulate.ccm282
9 files changed, 0 insertions, 1519 deletions
diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt
deleted file mode 100644
index 383dd0a..0000000
--- a/modules/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-# This source code is released into the public domain.
-
-add_library(nihil STATIC)
-target_sources(nihil PUBLIC
- FILE_SET modules TYPE CXX_MODULES FILES
- nihil.ccm
- ctype.ccm
- fd.ccm
- generator.ccm
- generic_error.ccm
- getenv.ccm
- guard.ccm
- tabulate.ccm)
diff --git a/modules/ctype.ccm b/modules/ctype.ccm
deleted file mode 100644
index cc058cd..0000000
--- a/modules/ctype.ccm
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <concepts>
-#include <locale>
-
-export module nihil:ctype;
-
-namespace nihil {
-
-/*
- * ctype_is: wrap std::ctype<T>::is() in a form suitable for use as an algorithm
- * predicate, i.e., ctype_is(m) will return a functor object that takes any char
- * type as an argument and returns bool.
- *
- * If the locale is not specified, the current global locale is used by default.
- *
- * ctype_is copies the locale, so passing a temporary is fine.
- */
-
-export struct ctype_is final {
- ctype_is(std::ctype_base::mask mask_,
- std::locale const &locale_ = std::locale())
- : mask(mask_)
- , locale(locale_)
- {}
-
- auto operator()(this ctype_is const &self, std::integral auto c)
- {
- using ctype = std::ctype<decltype(c)>;
- auto &facet = std::use_facet<ctype>(self.locale);
- return facet.is(self.mask, c);
- }
-
-private:
- std::ctype_base::mask mask;
- std::locale locale;
-};
-
-// Predefined tests for the current global locale.
-
-export inline auto is_space = ctype_is(std::ctype_base::space);
-export inline auto is_print = ctype_is(std::ctype_base::print);
-export inline auto is_cntrl = ctype_is(std::ctype_base::cntrl);
-export inline auto is_upper = ctype_is(std::ctype_base::upper);
-export inline auto is_lower = ctype_is(std::ctype_base::lower);
-export inline auto is_alpha = ctype_is(std::ctype_base::alpha);
-export inline auto is_digit = ctype_is(std::ctype_base::digit);
-export inline auto is_punct = ctype_is(std::ctype_base::punct);
-export inline auto is_xdigit = ctype_is(std::ctype_base::xdigit);
-export inline auto is_blank = ctype_is(std::ctype_base::blank);
-export inline auto is_alnum = ctype_is(std::ctype_base::alnum);
-export inline auto is_graph = ctype_is(std::ctype_base::graph);
-
-// Predefined tests for the C locale. The C locale is guaranteed to always be
-// available, so this doesn't create lifetime issues.
-
-export inline auto is_c_space =
- ctype_is(std::ctype_base::space, std::locale::classic());
-export inline auto is_c_print =
- ctype_is(std::ctype_base::print, std::locale::classic());
-export inline auto is_c_cntrl =
- ctype_is(std::ctype_base::cntrl, std::locale::classic());
-export inline auto is_c_upper =
- ctype_is(std::ctype_base::upper, std::locale::classic());
-export inline auto is_c_lower =
- ctype_is(std::ctype_base::lower, std::locale::classic());
-export inline auto is_c_alpha =
- ctype_is(std::ctype_base::alpha, std::locale::classic());
-export inline auto is_c_digit =
- ctype_is(std::ctype_base::digit, std::locale::classic());
-export inline auto is_c_punct =
- ctype_is(std::ctype_base::punct, std::locale::classic());
-export inline auto is_c_xdigit =
- ctype_is(std::ctype_base::xdigit, std::locale::classic());
-export inline auto is_c_blank =
- ctype_is(std::ctype_base::blank, std::locale::classic());
-export inline auto is_c_alnum =
- ctype_is(std::ctype_base::alnum, std::locale::classic());
-export inline auto is_c_graph =
- ctype_is(std::ctype_base::graph, std::locale::classic());
-
-} // namespace nihil
diff --git a/modules/fd.ccm b/modules/fd.ccm
deleted file mode 100644
index ad96ea7..0000000
--- a/modules/fd.ccm
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <expected>
-#include <format>
-#include <stdexcept>
-#include <system_error>
-
-export module nihil:fd;
-
-import :generic_error;
-
-namespace nihil {
-
-/*
- * Exception thrown when an internal fd error occurs. This is not supposed
- * to be caught, since it indicates an internal logic error in the caller.
- */
-export struct fd_logic_error final : std::logic_error {
- fd_logic_error(std::string what)
- : std::logic_error(std::move(what))
- {}
-};
-
-/*
- * fd: a file descriptor.
- */
-
-export struct fd final {
- // Construct an empty (invalid) fd.
- fd() noexcept = default;
-
- // Construct an fd from an exising file destrictor, taking ownership.
- fd(int fd_) noexcept : _fd(fd_) {}
-
- // Destructor. Close the fd, discarding any errors.
- ~fd()
- {
- if (*this)
- this->close();
- }
-
- // Move from another fd, leaving the moved-from fd in an invalid state.
- fd(fd &&other) noexcept
- : _fd(std::exchange(other._fd, _invalid_fd))
- {}
-
- // Move assign from another fd.
- auto operator=(fd &&other) noexcept -> fd &
- {
- if (this != &other)
- _fd = std::exchange(other._fd, _invalid_fd);
- return *this;
- }
-
- // Not copyable.
- fd(fd const &) = delete;
- fd& operator=(fd const &) = delete;
-
- // Return true if this fd is valid (open).
- explicit operator bool(this fd const &self) noexcept
- {
- return self._fd != _invalid_fd;
- }
-
- // Close the wrapped fd.
- auto close(this fd &self) -> std::expected<void, std::error_code>
- {
- auto const ret = ::close(self.get());
- self._fd = _invalid_fd;
-
- if (ret == 0)
- return {};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
- }
-
- // Return the stored fd.
- auto get(this fd const &self) -> int {
- if (self)
- return self._fd;
- throw fd_logic_error("Attempt to call get() on invalid fd");
- }
-
-
- // Release the stored fd and return it. The caller must close it.
- auto release(this fd &&self) -> int {
- if (self)
- return std::exchange(self._fd, self._invalid_fd);
- throw fd_logic_error("Attempt to release an invalid fd");
- }
-
-private:
- static constexpr int _invalid_fd = -1;
-
- int _fd = _invalid_fd;
-};
-
-// Create a copy of this fd by calling dup().
-export auto dup(fd const &self) -> std::expected<fd, std::error_code>
-{
- auto thisfd = self.get();
-
- auto const newfd = ::dup(thisfd);
- if (newfd != -1)
- return {newfd};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Create a copy of this fd by calling dup2(). Note that because this results
-// in the existing fd and the new fd both being managed by an fd instance,
-// there are two potential cases that can cause problems:
-//
-// - dup()ing an fd to itself (a no-op)
-// - dup()ing an fd to an fd which is already managed by an fd instance
-//
-// In both of these cases, either use raw_dup() instead, or immediately call
-// release() on the returned fd to prevent the fd instance from closing it.
-export auto dup(fd const &self, int newfd)
- -> std::expected<fd, std::error_code>
-{
- auto thisfd = self.get();
-
- auto const ret = ::dup2(thisfd, newfd);
- if (ret != -1)
- return {newfd};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Create a copy of this fd by calling dup().
-export auto raw_dup(fd const &self) -> std::expected<int, std::error_code>
-{
- auto thisfd = self.get();
-
- auto const newfd = ::dup(thisfd);
- if (newfd != -1)
- return {newfd};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Create a copy of this fd by calling dup2().
-export auto raw_dup(fd const &self, int newfd)
- -> std::expected<int, std::error_code>
-{
- auto thisfd = self.get();
-
- auto const ret = ::dup2(thisfd, newfd);
- if (ret != -1)
- return {newfd};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Return the fnctl flags for this fd.
-export auto getflags(fd const &self) -> std::expected<int, std::error_code>
-{
- auto const flags = ::fcntl(self.get(), F_GETFL);
- if (flags != -1)
- return {flags};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Replace the fnctl flags for this fd.
-export auto replaceflags(fd &self, int newflags)
- -> std::expected<void, std::error_code>
-{
- auto const ret = ::fcntl(self.get(), F_SETFL, newflags);
- if (ret == 0)
- return {};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Add bits to the fcntl flags for this fd. Returns the new flags.
-export auto setflags(fd &self, int newflags)
- -> std::expected<int, std::error_code>
-{
- auto flags = getflags(self);
- if (!flags)
- return flags;
-
- *flags |= newflags;
- auto const ret = replaceflags(self, *flags);
- if (!ret)
- return std::unexpected(ret.error());
-
- return {*flags};
-}
-
-// Remove bits from the fcntl flags for this fd. Returns the new flags.
-export auto clearflags(fd &self, int clrflags)
- -> std::expected<int, std::error_code>
-{
- auto flags = getflags(self);
- if (!flags)
- return flags;
-
- *flags &= ~clrflags;
- auto const ret = replaceflags(self, *flags);
- if (!ret)
- return std::unexpected(ret.error());
-
- return {*flags};
-}
-
-// Return the fd flags for this fd.
-export auto getfdflags(fd const &self) -> std::expected<int, std::error_code>
-{
- auto const flags = ::fcntl(self.get(), F_GETFD);
- if (flags != -1)
- return {flags};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Replace the fd flags for this fd.
-export auto replacefdflags(fd &self, int newflags)
- -> std::expected<void, std::error_code>
-{
- auto const ret = ::fcntl(self.get(), F_SETFD, newflags);
- if (ret != -1)
- return {};
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-// Add bits to the fd flags for this fd. Returns the new flags.
-export auto setfdflags(fd &self, int newflags)
- -> std::expected<int, std::error_code>
-{
- auto flags = getfdflags(self);
- if (!flags)
- return flags;
-
- *flags |= newflags;
- auto const ret = replacefdflags(self, *flags);
- if (!ret)
- return std::unexpected(ret.error());
-
- return {*flags};
-}
-
-// Remove bits from the fd flags for this fd. Returns the new flags.
-export auto clearfdflags(fd &self, int clrflags)
- -> std::expected<int, std::error_code>
-{
- auto flags = getfdflags(self);
- if (!flags)
- return flags;
-
- *flags &= ~clrflags;
- auto ret = replacefdflags(self, *flags);
- if (!ret)
- return std::unexpected(ret.error());
-
- return {*flags};
-}
-
-// Create two fds by calling pipe() and return them.
-export auto pipe() -> std::expected<std::pair<fd, fd>, std::error_code> {
- auto fds = std::array<int, 2>{};
-
- if (auto const ret = ::pipe(fds.data()); ret != 0)
- return std::unexpected(std::make_error_code(std::errc(errno)));
-
- return {{fd(fds[0]), fd(fds[1])}};
-}
-
-/*
- * Write data to a file descriptor from the provided buffer. Returns the
- * number of bytes (not objects) written. Incomplete writes may cause a
- * partial object to be written.
- */
-export auto write(fd &file, std::ranges::contiguous_range auto &&range)
- -> std::expected<std::size_t, std::error_code>
-{
- auto const ret = ::write(file.get(), std::ranges::data(range),
- std::ranges::size(range));
- if (ret >= 0)
- return ret;
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-/*
- * Read data from a file descriptor into the provided buffer. Returns the
- * number of bytes (not objects) read. Incomplete reads may cause a partial
- * object to be read.
- */
-export auto read(fd &file, std::ranges::contiguous_range auto &&range)
- -> std::expected<std::size_t, std::error_code>
-{
- auto const ret = ::read(file.get(), std::ranges::data(range),
- std::ranges::size(range));
- if (ret >= 0)
- return ret;
- return std::unexpected(std::make_error_code(std::errc(errno)));
-}
-
-} // namespace nihil
diff --git a/modules/generator.ccm b/modules/generator.ccm
deleted file mode 100644
index 82bcb27..0000000
--- a/modules/generator.ccm
+++ /dev/null
@@ -1,691 +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>
-#include <coroutine>
-#include <exception>
-#include <memory>
-#include <ranges>
-#include <type_traits>
-
-export module nihil: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);
-}
-
-
-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
-{
- template <typename _Ref2, typename _Value, typename _Alloc>
- friend class generator;
-
- __generator_promise_base* __root_;
- std::coroutine_handle<> __parentOrLeaf_;
- // 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> __exception_;
- __manual_lifetime<_Ref> __value_;
-
- explicit __generator_promise_base(std::coroutine_handle<> thisCoro) noexcept
- : __root_(this)
- , __parentOrLeaf_(thisCoro)
- {}
-
- ~__generator_promise_base() {
- if (__root_ != this) {
- // This coroutine was used as a nested generator and so will
- // have constructed its __exception_ member which needs to be
- // destroyed here.
- __exception_.destruct();
- }
- }
-
- std::suspend_always initial_suspend() noexcept {
- return {};
- }
-
- void return_void() noexcept {}
-
- void unhandled_exception() {
- if (__root_ != this) {
- __exception_.get() = std::current_exception();
- } else {
- throw;
- }
- }
-
- // Transfers control back to the parent of a nested coroutine
- struct __final_awaiter {
- bool await_ready() noexcept {
- return false;
- }
-
- template <typename _Promise>
- std::coroutine_handle<>
- await_suspend(std::coroutine_handle<_Promise> __h) noexcept {
- _Promise& __promise = __h.promise();
- __generator_promise_base& __root = *__promise.__root_;
- if (&__root != &__promise) {
- auto __parent = __promise.__parentOrLeaf_;
- __root.__parentOrLeaf_ = __parent;
- return __parent;
- }
- return std::noop_coroutine();
- }
-
- void await_resume() noexcept {}
- };
-
- __final_awaiter final_suspend() noexcept {
- return {};
- }
-
- std::suspend_always yield_value(_Ref&& __x)
- noexcept(std::is_nothrow_move_constructible_v<_Ref>) {
- __root_->__value_.construct((_Ref&&)__x);
- return {};
- }
-
- template <typename _T>
- requires
- (!std::is_reference_v<_Ref>) &&
- std::is_convertible_v<_T, _Ref>
- std::suspend_always yield_value(_T&& __x)
- noexcept(std::is_nothrow_constructible_v<_Ref, _T>) {
- __root_->__value_.construct((_T&&)__x);
- return {};
- }
-
- template <typename _Gen>
- struct __yield_sequence_awaiter {
- _Gen __gen_;
-
- __yield_sequence_awaiter(_Gen&& __g) noexcept
- // Taking ownership of the generator ensures frame are destroyed
- // in the reverse order of their execution.
- : __gen_((_Gen&&)__g) {
- }
-
- bool await_ready() noexcept {
- return false;
- }
-
- // set the parent, root and exceptions pointer and
- // resume the nested
- template<typename _Promise>
- std::coroutine_handle<>
- await_suspend(std::coroutine_handle<_Promise> __h) noexcept {
- __generator_promise_base& __current = __h.promise();
- __generator_promise_base& __nested = *__gen_.__get_promise();
- __generator_promise_base& __root = *__current.__root_;
-
- __nested.__root_ = __current.__root_;
- __nested.__parentOrLeaf_ = __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.__exception_.construct();
- __root.__parentOrLeaf_ = __gen_.__get_coro();
-
- // Immediately resume the nested coroutine (nested generator)
- return __gen_.__get_coro();
- }
-
- void await_resume() {
- __generator_promise_base& __nestedPromise = *__gen_.__get_promise();
- if (__nestedPromise.__exception_.get()) {
- std::rethrow_exception(std::move(__nestedPromise.__exception_.get()));
- }
- }
- };
-
- template <typename _OValue, typename _OAlloc>
- __yield_sequence_awaiter<generator<_Ref, _OValue, _OAlloc>>
- yield_value(nihil::ranges::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) {
- 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() {
- __parentOrLeaf_.resume();
- }
-
- // Disable use of co_await within this coroutine.
- void await_transform() = delete;
-};
-
-template<typename _Generator, typename _ByteAllocator, bool _ExplicitAllocator = false>
-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> {
- __generator_promise() noexcept
- : __generator_promise_base<_Ref>(std::coroutine_handle<__generator_promise>::from_promise(*this))
- {}
-
- generator<_Ref, _Value, _Alloc> get_return_object() noexcept {
- 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>
- typename __generator_promise_base<_Ref>::template __yield_sequence_awaiter<generator<_Ref, _Value, _Alloc>>
- yield_value(nihil::ranges::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");
- return [](auto && __rng) -> generator<_Ref, _Value, _Alloc> {
- for(auto && e: __rng)
- co_yield static_cast<decltype(e)>(e);
- }(std::forward<_Rng>(__x.get()));
- }
-};
-
-template<typename _Alloc>
-using __byte_allocator_t = typename std::allocator_traits<std::remove_cvref_t<_Alloc>>::template rebind_alloc<std::byte>;
-
-} // namespace nihil
-
-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
-
-namespace nihil {
-
-// TODO : make layout compatible promise casts possible
-export template <typename _Ref, typename _Value, typename _Alloc>
-class 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
- : __coro_(std::exchange(__other.__coro_, {}))
- , __started_(std::exchange(__other.__started_, false)) {
- }
-
- ~generator() noexcept {
- if (__coro_) {
- if (__started_ && !__coro_.done()) {
- __coro_.promise().__value_.destruct();
- }
- __coro_.destroy();
- }
- }
-
- generator& operator=(generator && g) noexcept {
- swap(g);
- return *this;
- }
-
- void swap(generator& __other) noexcept {
- std::swap(__coro_, __other.__coro_);
- std::swap(__started_, __other.__started_);
- }
-
- struct sentinel {};
-
- class iterator {
- public:
- 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
- : __coro_(std::exchange(__other.__coro_, {})) {
- }
-
- iterator& operator=(iterator&& __other) {
- std::swap(__coro_, __other.__coro_);
- return *this;
- }
-
- ~iterator() {
- }
-
- friend bool operator==(const iterator &it, sentinel) noexcept {
- return it.__coro_.done();
- }
-
- iterator &operator++() {
- __coro_.promise().__value_.destruct();
- __coro_.promise().resume();
- return *this;
- }
- void operator++(int) {
- (void)operator++();
- }
-
- reference operator*() const noexcept {
- return static_cast<reference>(__coro_.promise().__value_.get());
- }
-
- private:
- friend generator;
-
- explicit iterator(__coroutine_handle __coro) noexcept
- : __coro_(__coro) {}
-
- __coroutine_handle __coro_;
- };
-
- iterator begin() {
- assert(__coro_);
- assert(!__started_);
- __started_ = true;
- __coro_.resume();
- return iterator{__coro_};
- }
-
- sentinel end() noexcept {
- return {};
- }
-
-private:
- explicit generator(__coroutine_handle __coro) noexcept
- : __coro_(__coro) {
- }
-
-public: // to get around access restrictions for __yield_sequence_awaitable
- std::coroutine_handle<> __get_coro() noexcept { return __coro_; }
- promise_type* __get_promise() noexcept { return std::addressof(__coro_.promise()); }
-
-private:
- __coroutine_handle __coro_;
- bool __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
- : __promise_(nullptr)
- , __coro_()
- , __started_(false)
- {}
-
- generator(generator&& __other) noexcept
- : __promise_(std::exchange(__other.__promise_, nullptr))
- , __coro_(std::exchange(__other.__coro_, {}))
- , __started_(std::exchange(__other.__started_, false)) {
- }
-
- ~generator() noexcept {
- if (__coro_) {
- if (__started_ && !__coro_.done()) {
- __promise_->__value_.destruct();
- }
- __coro_.destroy();
- }
- }
-
- generator& operator=(generator g) noexcept {
- swap(g);
- return *this;
- }
-
- void swap(generator& __other) noexcept {
- std::swap(__promise_, __other.__promise_);
- std::swap(__coro_, __other.__coro_);
- std::swap(__started_, __other.__started_);
- }
-
- struct sentinel {};
-
- class iterator {
- public:
- 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
- : __promise_(std::exchange(__other.__promise_, nullptr))
- , __coro_(std::exchange(__other.__coro_, {}))
- {}
-
- iterator& operator=(iterator&& __other) {
- __promise_ = std::exchange(__other.__promise_, nullptr);
- __coro_ = std::exchange(__other.__coro_, {});
- return *this;
- }
-
- ~iterator() = default;
-
- friend bool operator==(const iterator &it, sentinel) noexcept {
- return it.__coro_.done();
- }
-
- iterator& operator++() {
- __promise_->__value_.destruct();
- __promise_->resume();
- return *this;
- }
-
- void operator++(int) {
- (void)operator++();
- }
-
- reference operator*() const noexcept {
- return static_cast<reference>(__promise_->__value_.get());
- }
-
- private:
- friend generator;
-
- explicit iterator(__promise_base* __promise, std::coroutine_handle<> __coro) noexcept
- : __promise_(__promise)
- , __coro_(__coro)
- {}
-
- __promise_base* __promise_;
- std::coroutine_handle<> __coro_;
- };
-
- iterator begin() {
- assert(__coro_);
- assert(!__started_);
- __started_ = true;
- __coro_.resume();
- return iterator{__promise_, __coro_};
- }
-
- sentinel end() noexcept {
- return {};
- }
-
-private:
- template<typename _Generator, typename _ByteAllocator, bool _ExplicitAllocator>
- friend struct __generator_promise;
-
- template<typename _Promise>
- explicit generator(std::coroutine_handle<_Promise> __coro) noexcept
- : __promise_(std::addressof(__coro.promise()))
- , __coro_(__coro)
- {}
-
-public: // to get around access restrictions for __yield_sequence_awaitable
- std::coroutine_handle<> __get_coro() noexcept { return __coro_; }
- __promise_base* __get_promise() noexcept { return __promise_; }
-
-private:
- __promise_base* __promise_;
- std::coroutine_handle<> __coro_;
- bool __started_ = false;
-};
-
-} // 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/modules/generic_error.ccm b/modules/generic_error.ccm
deleted file mode 100644
index a582519..0000000
--- a/modules/generic_error.ccm
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <format>
-#include <stdexcept>
-
-export module nihil:generic_error;
-
-namespace nihil {
-
-/*
- * generic_error is the base class that all other exceptions derive from.
- * It is an std::runtime_error, and what() should always be informative.
- */
-
-export struct generic_error : std::runtime_error {
- template<typename... Args>
- generic_error(std::format_string<Args...> fmt, Args &&...args)
- : std::runtime_error(std::format(fmt, std::forward<Args>(args)...))
- {}
-};
-
-} // namespace nihil
diff --git a/modules/getenv.ccm b/modules/getenv.ccm
deleted file mode 100644
index 7397b79..0000000
--- a/modules/getenv.ccm
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <cstdint>
-#include <expected>
-#include <string>
-#include <system_error>
-#include <vector>
-
-#include <unistd.h>
-
-export module nihil:getenv;
-
-namespace nihil {
-
-/*
- * Find a variable by the given name in the environment by calling getenv_r().
- */
-
-export auto getenv(std::string_view varname)
- -> std::expected<std::string, std::error_code>
-{
- // Start with a buffer of this size, and double it every iteration.
- constexpr auto bufinc = std::size_t{1024};
-
- auto cvarname = std::string(varname);
- auto buf = std::vector<char>(bufinc);
- for (;;) {
- auto const ret = ::getenv_r(cvarname.c_str(),
- buf.data(), buf.size());
-
- if (ret == 0)
- return {std::string(buf.data())};
-
- if (ret == -1 && errno == ERANGE) {
- buf.resize(buf.size() * 2);
- continue;
- }
-
- return std::unexpected(std::make_error_code(std::errc(errno)));
- }
-}
-
-} // namespace nihil
diff --git a/modules/guard.ccm b/modules/guard.ccm
deleted file mode 100644
index 18c6d70..0000000
--- a/modules/guard.ccm
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <concepts>
-#include <optional>
-#include <utility>
-
-export module nihil:guard;
-
-namespace nihil {
-
-/*
- * guard: invoke a callable when this object is destroyed; this is similar to
- * scope_exit from the library fundamentals TS, which LLVM doesn't implement.
- */
-export template<std::invocable F>
-struct guard final {
- // Initialise the guard with a callable we will invoke later.
- guard(F func) : _func(std::move(func)) {}
-
- /*
- * We are being destroyed, so call the callable.
- * If the callable throws, std::terminate() will be called.
- */
- ~guard() {
- if (_func)
- std::invoke(*_func);
- }
-
- // Release the guard. This turns the destructor into a no-op.
- void release() noexcept {
- _func.reset();
- }
-
- // Not default-constructible or copyable.
- guard() = delete;
- guard(guard const &) = delete;
- guard(guard &&) noexcept = delete;
- guard &operator=(guard const &) = delete;
- guard &operator=(guard &&) noexcept = delete;
-
-private:
- // The callable to be invoked when we are destroyed.
- std::optional<F> _func;
-};
-
-} // namespace nihil
diff --git a/modules/nihil.ccm b/modules/nihil.ccm
deleted file mode 100644
index 69cc282..0000000
--- a/modules/nihil.ccm
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-export module nihil;
-
-export import :ctype;
-export import :generator;
-export import :generic_error;
-export import :getenv;
-export import :guard;
-export import :fd;
-export import :tabulate;
diff --git a/modules/tabulate.ccm b/modules/tabulate.ccm
deleted file mode 100644
index debb784..0000000
--- a/modules/tabulate.ccm
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <algorithm>
-#include <cstdlib>
-#include <format>
-#include <ranges>
-#include <iterator>
-#include <vector>
-
-export module nihil:tabulate;
-
-import :ctype;
-import :generic_error;
-
-namespace nihil {
-
-/*
- * tabulate: format the given range in an ASCII table and write the output
- * to the given output iterator. The range's values will be converted to
- * strings as if by std::format.
- *
- * tabulate is implemented by copying the range; this allows it to work on
- * input/forward ranges at the cost of slightly increased memory use.
- *
- * The table spec is a string consisting of zero or more field formats,
- * formatted as {flags:fieldname}; both flags and fieldname are optional.
- * If there are fewer field formats than fields, the remaining fields
- * are formatted as if by {:}.
- *
- * The following flags are supported:
- *
- * < left-align this column (default)
- * > right-align this column
- */
-
-// Exception thrown when a table spec is invalid.
-export struct table_spec_error : generic_error {
- template<typename... Args>
- table_spec_error(std::format_string<Args...> fmt, Args &&...args)
- : generic_error(fmt, std::forward<Args>(args)...)
- {}
-};
-
-/*
- * The specification for a single field.
- */
-template<typename Char>
-struct field_spec {
- std::basic_string_view<Char> name;
- std::size_t width = 0;
- enum { left, right } align = left;
-
- // Ensure the length of this field is at least the given width.
- auto ensure_width(std::size_t newwidth) -> void
- {
- width = std::max(width, newwidth);
- }
-
- // Format an object to a string based on our field spec.
- auto format(auto &&obj) const -> std::basic_string<Char>
- {
- std::basic_string<Char> format_string{'{', '}'};
- return std::format(std::runtime_format(format_string), obj);
- }
-
- // Print a column value to an output iterator according to our field
- // spec. If is_last is true, this is the last field on the line, so
- // we won't output any trailling padding.
- auto print(std::basic_string_view<Char> value,
- std::output_iterator<Char> auto &out,
- bool is_last)
- const
- {
- auto padding = width - value.size();
-
- if (align == right)
- for (std::size_t i = 0; i < padding; ++i)
- *out++ = ' ';
-
- std::ranges::copy(value, out);
-
- if (!is_last && align == left)
- for (std::size_t i = 0; i < padding; ++i)
- *out++ = ' ';
- }
-};
-
-/*
- * The specification for an entire table.
- */
-template<typename Char>
-struct table_spec {
- // Add a new field spec to this table.
- auto add(field_spec<Char> field)
- {
- _fields.emplace_back(std::move(field));
- }
-
- // Return the field spec for a given field. If the field doesn't
- // exist, this field and any intermediate fields will be created.
- auto field(std::size_t fieldnr) -> field_spec<Char>&
- {
- if (_fields.size() < fieldnr + 1)
- _fields.resize(fieldnr + 1);
- return _fields.at(fieldnr);
- }
-
- // The number of columns in this table.
- auto columns() const -> std::size_t
- {
- return _fields.size();
- }
-
- // Return all the fields in this table.
- auto fields() const
- {
- return _fields;
- }
-
-private:
- std::vector<field_spec<Char>> _fields;
-};
-
-// Parse the field flags, e.g. '<'.
-template<typename Char,
- std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
-auto parse_field_flags(field_spec<Char> &field, Iterator &pos, Sentinel end)
- -> void
-{
- while (pos < end) {
- switch (*pos) {
- case '<':
- field.align = field_spec<Char>::left;
- break;
- case '>':
- field.align = field_spec<Char>::right;
- break;
- case ':':
- ++pos;
- /*FALLTHROUGH*/
- case '}':
- return;
- default:
- throw table_spec_error(
- "Invalid table spec: unknown flag character");
- }
-
- if (++pos == end)
- throw table_spec_error("Invalid table spec: "
- "unterminated field");
- }
-}
-
-// Parse a complete field spec, e.g. "{<:NAME}".
-template<typename Char,
- std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
-auto parse_field(Iterator &pos, Sentinel end)
- -> field_spec<Char>
-{
- auto field = field_spec<Char>{};
-
- if (pos == end)
- throw table_spec_error("Invalid table spec: empty field");
-
- // The field spec should start with a '{'.
- if (*pos != '{')
- throw table_spec_error("Invalid table spec: expected '{{'");
-
- if (++pos == end)
- throw table_spec_error("Invalid table spec: unterminated field");
-
- // This consumes 'pos' up to and including the ':'.
- parse_field_flags(field, pos, end);
-
- auto brace = std::ranges::find(pos, end, '}');
- if (brace == end)
- throw table_spec_error("Invalid table spec: expected '}}'");
-
- field.name = std::basic_string_view<Char>(pos, brace);
- pos = std::next(brace);
-
- // The field must be at least as wide as its header.
- field.width = field.name.size();
-
- return field;
-}
-
-template<typename Char>
-auto parse_table_spec(std::basic_string_view<Char> spec) -> table_spec<Char>
-{
- auto table = table_spec<Char>();
-
- auto pos = std::ranges::begin(spec);
- auto end = std::ranges::end(spec);
-
- for (;;) {
- // Skip leading whitespace
- while (pos < end && is_c_space(*pos))
- ++pos;
-
- if (pos == end)
- break;
-
- table.add(parse_field<Char>(pos, end));
- }
-
- return table;
-}
-
-export template<typename Char,
- std::ranges::range Range,
- std::output_iterator<Char> Iterator>
-auto basic_tabulate(std::basic_string_view<Char> table_spec,
- Range &&range,
- Iterator &&out)
- -> void
-{
- // Parse the table spec.
- auto table = parse_table_spec(table_spec);
-
- // Create our copy of the input data.
- auto data = std::vector<std::vector<std::basic_string<Char>>>();
- // Reserve the first row for the header.
- data.resize(1);
-
- // Find the required length of each field.
- for (auto &&row : range) {
- // LLVM doesn't have std::enumerate_view yet
- auto i = std::size_t{0};
- auto &this_row = data.emplace_back();
-
- for (auto &&column : row) {
- auto &field = table.field(i);
- auto &str = this_row.emplace_back(field.format(column));
- field.ensure_width(str.size());
- ++i;
- }
- }
-
- // Add the header row.
- for (auto &&field : table.fields())
- data.at(0).emplace_back(std::from_range, field.name);
-
- // Print the values.
- for (auto &&row : data) {
- for (std::size_t i = 0; i < row.size(); ++i) {
- auto &field = table.field(i);
- bool is_last = (i == row.size() - 1);
-
- field.print(row[i], out, is_last);
-
- if (!is_last)
- *out++ = ' ';
- }
-
- *out++ = '\n';
- }
-}
-
-export auto tabulate(std::string_view table_spec,
- std::ranges::range auto &&range,
- std::output_iterator<char> auto &&out)
-{
- return basic_tabulate<char>(table_spec,
- std::forward<decltype(range)>(range),
- std::forward<decltype(out)>(out));
-}
-
-export auto wtabulate(std::wstring_view table_spec,
- std::ranges::range auto &&range,
- std::output_iterator<wchar_t> auto &&out)
-{
- return basic_tabulate<wchar_t>(table_spec,
- std::forward<decltype(range)>(range),
- std::forward<decltype(out)>(out));
-}
-
-} // namespace nihil