From bde0492644845de63cf95b8997c5e613a9247826 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Wed, 2 Jul 2025 03:10:07 +0100 Subject: move flagset to util --- CMakeLists.txt | 1 - nihil.error/error.ccm | 2 +- nihil.flagset/CMakeLists.txt | 22 ----- nihil.flagset/flagset.ccm | 200 ------------------------------------------- nihil.flagset/test.cc | 144 ------------------------------- nihil.posix/CMakeLists.txt | 1 - nihil.posix/fd.ccm | 2 +- nihil.posix/open.ccm | 1 - nihil.posix/tempfile.ccm | 2 +- nihil.util/CMakeLists.txt | 2 + nihil.util/flagset.ccm | 200 +++++++++++++++++++++++++++++++++++++++++++ nihil.util/flagset.test.cc | 144 +++++++++++++++++++++++++++++++ nihil.util/nihil.util.ccm | 1 + 13 files changed, 350 insertions(+), 372 deletions(-) delete mode 100644 nihil.flagset/CMakeLists.txt delete mode 100644 nihil.flagset/flagset.ccm delete mode 100644 nihil.flagset/test.cc create mode 100644 nihil.util/flagset.ccm create mode 100644 nihil.util/flagset.test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index bd684ef..8c02a3e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,6 @@ add_subdirectory(nihil.std) add_subdirectory(nihil.cli) add_subdirectory(nihil.core) add_subdirectory(nihil.error) -add_subdirectory(nihil.flagset) add_subdirectory(nihil.generator) add_subdirectory(nihil.match) add_subdirectory(nihil.monad) diff --git a/nihil.error/error.ccm b/nihil.error/error.ccm index 2d5166a..833f7f1 100644 --- a/nihil.error/error.ccm +++ b/nihil.error/error.ccm @@ -225,7 +225,7 @@ export struct error : std::exception // Allow error to be implicitly converted to std::expected and std::unexpected, to make // using it with std::expected easier. - template + template operator std::expected () && // NOLINT { return std::unexpected{std::move(*this)}; diff --git a/nihil.flagset/CMakeLists.txt b/nihil.flagset/CMakeLists.txt deleted file mode 100644 index 40d106e..0000000 --- a/nihil.flagset/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# This source code is released into the public domain. - -add_library(nihil.flagset STATIC) -target_link_libraries(nihil.flagset PRIVATE nihil.std) -target_sources(nihil.flagset - PUBLIC FILE_SET modules TYPE CXX_MODULES FILES - flagset.ccm -) - -if(NIHIL_TESTS) - enable_testing() - - add_executable(nihil.flagset.test test.cc) - target_link_libraries(nihil.flagset.test PRIVATE - nihil.flagset - Catch2::Catch2WithMain - ) - - include(CTest) - include(Catch) - catch_discover_tests(nihil.flagset.test) -endif() diff --git a/nihil.flagset/flagset.ccm b/nihil.flagset/flagset.ccm deleted file mode 100644 index 796a2c2..0000000 --- a/nihil.flagset/flagset.ccm +++ /dev/null @@ -1,200 +0,0 @@ -// This source code is released into the public domain. -export module nihil.flagset; - -/* - * flagset: a type-safe flags type. - */ - -import nihil.std; - -namespace nihil { - -export template -struct flagset final -{ - using underlying_type = base_type; - - /* - * Create an empty flags. - */ - flagset() noexcept = default; - - /* - * Copyable. - */ - flagset(flagset const &other) noexcept - : m_value(other.m_value) - { - } - - /* - * Create flags from an integer mask. - */ - template - [[nodiscard]] static constexpr auto mask() noexcept -> flagset - { - return flagset(flag); - } - - /* - * Create flags for a specific bit. - */ - template - [[nodiscard]] static constexpr auto bit() noexcept -> flagset - { - static_assert(bitnr < std::numeric_limits::digits); - return flagset(static_cast(1) << bitnr); - } - - /* - * Create flags from a runtime value. - */ - [[nodiscard]] static auto from_int(base_type value) noexcept -> flagset - { - return flagset(value); - } - - /* - * Assign this flagset. - */ - auto operator=(this flagset &lhs, flagset rhs) noexcept -> flagset & - { - if (&lhs != &rhs) - lhs.m_value = rhs.m_value; - return lhs; - } - - /* - * The integer value of this flagset. - */ - [[nodiscard]] constexpr auto value(this flagset self) noexcept -> base_type - { - return self.m_value; - } - - /* - * True if this flagset has any bits set. - */ - [[nodiscard]] explicit constexpr operator bool(this flagset self) noexcept - { - return self.m_value != 0; - } - - /* - * Set bits. - */ - constexpr auto operator|=(this flagset &lhs, flagset rhs) noexcept -> flagset & - { - lhs.m_value |= rhs.value(); - return lhs; - } - - /* - * Mask bits. - */ - constexpr auto operator&=(this flagset &lhs, flagset rhs) noexcept -> flagset & - { - lhs.m_value &= rhs.value(); - return lhs; - } - - /* - * Invert bits. - */ - [[nodiscard]] constexpr auto operator~(this flagset self) noexcept -> flagset - { - return flagset(~self.m_value); - } - - /* - * xor bits. - */ - constexpr auto operator^=(this flagset &lhs, flagset rhs) noexcept -> flagset - { - lhs.m_value ^= rhs.value(); - return lhs; - } - -private: - base_type m_value = 0; - - explicit constexpr flagset(base_type mask) noexcept - : m_value(mask) - { - } - - [[nodiscard]] friend auto operator|(flagset lhs, flagset rhs) noexcept -> flagset - { - return (lhs |= rhs); - } - - [[nodiscard]] friend auto operator&(flagset lhs, flagset rhs) noexcept -> flagset - { - return (lhs &= rhs); - } - - [[nodiscard]] friend auto operator^(flagset lhs, flagset rhs) noexcept -> flagset - { - return (lhs ^= rhs); - } - - [[nodiscard]] friend auto - operator==(flagset lhs, flagset rhs) noexcept -> bool - { - return lhs.value() == rhs.value(); - } - - friend auto operator<<(std::ostream &strm, flagset flags) -> std::ostream & - { - std::print(strm, "{}", flags); - return strm; - } -}; - -} // namespace nihil - -/* - * Formatting for flagset. - */ -export template -struct std::formatter, Char> -{ - using flags_t = nihil::flagset; - - template - constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator - { - return ctx.begin(); - } - - template - auto format(flags_t flags, FmtContext &ctx) const -> FmtContext::iterator - { - auto constexpr digits = std::numeric_limits::digits; - auto value = flags.value(); - auto it = ctx.out(); - *it++ = Char{'<'}; - - auto printed_any = false; - - for (unsigned i = 0; i < digits; ++i) { - unsigned bit = (digits - 1) - i; - - auto this_bit = static_cast(1) << bit; - if ((value & this_bit) == 0) - continue; - - if (printed_any) - *it++ = Char{','}; - - if (bit > 10) - *it++ = Char{'0'} + (bit / 10); - *it++ = Char{'0'} + (bit % 10); - - printed_any = true; - } - - *it++ = Char{'>'}; - return it; - } -}; diff --git a/nihil.flagset/test.cc b/nihil.flagset/test.cc deleted file mode 100644 index 90eef4a..0000000 --- a/nihil.flagset/test.cc +++ /dev/null @@ -1,144 +0,0 @@ -// This source code is released into the public domain. - -#include - -import nihil.std; -import nihil.flagset; - -namespace { -struct test_tag -{ -}; -using testflags = nihil::flagset; - -constexpr auto zero = testflags::bit<0>(); -constexpr auto one = testflags::bit<1>(); -constexpr auto two = testflags::bit<2>(); -constexpr auto twelve = testflags::bit<12>(); - -TEST_CASE("flagset: invariant", "[nihil]") -{ - static_assert(std::regular); - static_assert(sizeof(testflags) == sizeof(testflags::underlying_type)); -} - -TEST_CASE("flagset: bit<>", "[nihil]") -{ - REQUIRE(zero.value() == 0x1); - REQUIRE(one.value() == 0x2); -} - -TEST_CASE("flagset: mask<>", "[nihil]") -{ - auto zero_ = testflags::mask<0x1>(); - auto one_ = testflags::mask<0x2>(); - - REQUIRE(zero_ == zero); - REQUIRE(one_ == one); -} - -TEST_CASE("flagset: constructor", "[nihil]") -{ - SECTION ("default construct") { - auto flags = testflags(); - REQUIRE(flags.value() == 0); - } - - SECTION ("construct from int") { - auto flags = testflags::from_int(one.value() | zero.value()); - REQUIRE(flags == (one | zero)); - } - - SECTION ("copy construct") { - auto flags = one; - auto flags2(flags); - REQUIRE(flags == flags2); - } -} - -TEST_CASE("flagset: operators", "[nihil]") -{ - SECTION ("operator|") { - REQUIRE((zero | one).value() == 0x3); - } - - SECTION ("operator|=") { - auto flags = zero; - flags |= one; - REQUIRE(flags.value() == 0x3); - } - - SECTION ("operator&") { - auto flags = zero | one; - - REQUIRE((flags & zero) == zero); - } - - SECTION ("operator&=") { - auto flags = zero | one | two; - REQUIRE(flags.value() == 0x7); - flags &= (zero | one); - REQUIRE(flags.value() == 0x3); - } - - SECTION ("operator^") { - auto flags = zero | one; - REQUIRE((flags ^ (one | two)) == (zero | two)); - } - - SECTION ("operator^=") { - auto flags = zero | one; - flags ^= (one | two); - REQUIRE(flags == (zero | two)); - } - - SECTION ("operator~") { - auto flags = ~zero; - REQUIRE(flags.value() == ~static_cast(1)); - } - - SECTION ("operator==") { - auto flags = zero; - REQUIRE(flags == zero); - REQUIRE(flags != one); - } -} - -TEST_CASE("flagset: assignment", "[nihil]") -{ - auto flags = zero; - REQUIRE(flags == zero); - - flags = one; - REQUIRE(flags == one); - REQUIRE(flags != zero); -} - -TEST_CASE("flagset: format", "[nihil]") -{ - REQUIRE(std::format("{}", testflags()) == "<>"); - REQUIRE(std::format("{}", zero) == "<0>"); - REQUIRE(std::format("{}", one) == "<1>"); - REQUIRE(std::format("{}", zero | one) == "<1,0>"); - - REQUIRE(std::format("{}", twelve) == "<12>"); - REQUIRE(std::format("{}", twelve | one) == "<12,1>"); -} - -TEST_CASE("flagset: ostream operator<<", "[nihil]") -{ - auto write = [](testflags flags) -> std::string { - auto strm = std::ostringstream(); - strm << flags; - return strm.str(); - }; - - REQUIRE(write(testflags()) == "<>"); - REQUIRE(write(zero) == "<0>"); - REQUIRE(write(one) == "<1>"); - REQUIRE(write(zero | one) == "<1,0>"); - - REQUIRE(write(twelve) == "<12>"); - REQUIRE(write(twelve | one) == "<12,1>"); -} -} // anonymous namespace diff --git a/nihil.posix/CMakeLists.txt b/nihil.posix/CMakeLists.txt index 7ce4d13..c4a3556 100644 --- a/nihil.posix/CMakeLists.txt +++ b/nihil.posix/CMakeLists.txt @@ -5,7 +5,6 @@ target_link_libraries(nihil.posix PRIVATE nihil.std nihil.core nihil.error - nihil.flagset nihil.monad nihil.util ) diff --git a/nihil.posix/fd.ccm b/nihil.posix/fd.ccm index 8210b6d..40bfb0a 100644 --- a/nihil.posix/fd.ccm +++ b/nihil.posix/fd.ccm @@ -7,9 +7,9 @@ module; export module nihil.posix:fd; import nihil.std; -import nihil.flagset; import nihil.error; import nihil.monad; +import nihil.util; namespace nihil { diff --git a/nihil.posix/open.ccm b/nihil.posix/open.ccm index a5a9e5d..ab3d6e1 100644 --- a/nihil.posix/open.ccm +++ b/nihil.posix/open.ccm @@ -8,7 +8,6 @@ export module nihil.posix:open; import nihil.std; import nihil.error; -import nihil.flagset; import nihil.util; import :fd; diff --git a/nihil.posix/tempfile.ccm b/nihil.posix/tempfile.ccm index 121c636..a4d3756 100644 --- a/nihil.posix/tempfile.ccm +++ b/nihil.posix/tempfile.ccm @@ -3,7 +3,7 @@ export module nihil.posix:tempfile; import nihil.std; import nihil.error; -import nihil.flagset; +import nihil.util; import :fd; import :getenv; import :open; diff --git a/nihil.util/CMakeLists.txt b/nihil.util/CMakeLists.txt index 5d6be7c..2755103 100644 --- a/nihil.util/CMakeLists.txt +++ b/nihil.util/CMakeLists.txt @@ -14,6 +14,7 @@ target_sources(nihil.util capture_stream.ccm construct.ccm ctype.ccm + flagset.ccm guard.ccm parse_size.ccm next_word.ccm @@ -28,6 +29,7 @@ if(NIHIL_TESTS) add_executable(nihil.util.test capture_stream.test.cc ctype.test.cc + flagset.test.cc guard.test.cc parse_size.test.cc next_word.test.cc diff --git a/nihil.util/flagset.ccm b/nihil.util/flagset.ccm new file mode 100644 index 0000000..4c42223 --- /dev/null +++ b/nihil.util/flagset.ccm @@ -0,0 +1,200 @@ +// This source code is released into the public domain. +export module nihil.util:flagset; + +/* + * flagset: a type-safe flags type. + */ + +import nihil.std; + +namespace nihil { + +export template +struct flagset final +{ + using underlying_type = base_type; + + /* + * Create an empty flags. + */ + flagset() noexcept = default; + + /* + * Copyable. + */ + flagset(flagset const &other) noexcept + : m_value(other.m_value) + { + } + + /* + * Create flags from an integer mask. + */ + template + [[nodiscard]] static constexpr auto mask() noexcept -> flagset + { + return flagset(flag); + } + + /* + * Create flags for a specific bit. + */ + template + [[nodiscard]] static constexpr auto bit() noexcept -> flagset + { + static_assert(bitnr < std::numeric_limits::digits); + return flagset(static_cast(1) << bitnr); + } + + /* + * Create flags from a runtime value. + */ + [[nodiscard]] static auto from_int(base_type value) noexcept -> flagset + { + return flagset(value); + } + + /* + * Assign this flagset. + */ + auto operator=(this flagset &lhs, flagset rhs) noexcept -> flagset & + { + if (&lhs != &rhs) + lhs.m_value = rhs.m_value; + return lhs; + } + + /* + * The integer value of this flagset. + */ + [[nodiscard]] constexpr auto value(this flagset self) noexcept -> base_type + { + return self.m_value; + } + + /* + * True if this flagset has any bits set. + */ + [[nodiscard]] explicit constexpr operator bool(this flagset self) noexcept + { + return self.m_value != 0; + } + + /* + * Set bits. + */ + constexpr auto operator|=(this flagset &lhs, flagset rhs) noexcept -> flagset & + { + lhs.m_value |= rhs.value(); + return lhs; + } + + /* + * Mask bits. + */ + constexpr auto operator&=(this flagset &lhs, flagset rhs) noexcept -> flagset & + { + lhs.m_value &= rhs.value(); + return lhs; + } + + /* + * Invert bits. + */ + [[nodiscard]] constexpr auto operator~(this flagset self) noexcept -> flagset + { + return flagset(~self.m_value); + } + + /* + * xor bits. + */ + constexpr auto operator^=(this flagset &lhs, flagset rhs) noexcept -> flagset + { + lhs.m_value ^= rhs.value(); + return lhs; + } + +private: + base_type m_value = 0; + + explicit constexpr flagset(base_type mask) noexcept + : m_value(mask) + { + } + + [[nodiscard]] friend auto operator|(flagset lhs, flagset rhs) noexcept -> flagset + { + return (lhs |= rhs); + } + + [[nodiscard]] friend auto operator&(flagset lhs, flagset rhs) noexcept -> flagset + { + return (lhs &= rhs); + } + + [[nodiscard]] friend auto operator^(flagset lhs, flagset rhs) noexcept -> flagset + { + return (lhs ^= rhs); + } + + [[nodiscard]] friend auto + operator==(flagset lhs, flagset rhs) noexcept -> bool + { + return lhs.value() == rhs.value(); + } + + friend auto operator<<(std::ostream &strm, flagset flags) -> std::ostream & + { + std::print(strm, "{}", flags); + return strm; + } +}; + +} // namespace nihil + +/* + * Formatting for flagset. + */ +export template +struct std::formatter, Char> +{ + using flags_t = nihil::flagset; + + template + constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator + { + return ctx.begin(); + } + + template + auto format(flags_t flags, FmtContext &ctx) const -> FmtContext::iterator + { + auto constexpr digits = std::numeric_limits::digits; + auto value = flags.value(); + auto it = ctx.out(); + *it++ = Char{'<'}; + + auto printed_any = false; + + for (unsigned i = 0; i < digits; ++i) { + unsigned bit = (digits - 1) - i; + + auto this_bit = static_cast(1) << bit; + if ((value & this_bit) == 0) + continue; + + if (printed_any) + *it++ = Char{','}; + + if (bit > 10) + *it++ = Char{'0'} + (bit / 10); + *it++ = Char{'0'} + (bit % 10); + + printed_any = true; + } + + *it++ = Char{'>'}; + return it; + } +}; diff --git a/nihil.util/flagset.test.cc b/nihil.util/flagset.test.cc new file mode 100644 index 0000000..85cd0d3 --- /dev/null +++ b/nihil.util/flagset.test.cc @@ -0,0 +1,144 @@ +// This source code is released into the public domain. + +#include + +import nihil.std; +import nihil.util; + +namespace { +struct test_tag +{ +}; +using testflags = nihil::flagset; + +constexpr auto zero = testflags::bit<0>(); +constexpr auto one = testflags::bit<1>(); +constexpr auto two = testflags::bit<2>(); +constexpr auto twelve = testflags::bit<12>(); + +TEST_CASE("flagset: invariant", "[nihil]") +{ + static_assert(std::regular); + static_assert(sizeof(testflags) == sizeof(testflags::underlying_type)); +} + +TEST_CASE("flagset: bit<>", "[nihil]") +{ + REQUIRE(zero.value() == 0x1); + REQUIRE(one.value() == 0x2); +} + +TEST_CASE("flagset: mask<>", "[nihil]") +{ + auto zero_ = testflags::mask<0x1>(); + auto one_ = testflags::mask<0x2>(); + + REQUIRE(zero_ == zero); + REQUIRE(one_ == one); +} + +TEST_CASE("flagset: constructor", "[nihil]") +{ + SECTION ("default construct") { + auto flags = testflags(); + REQUIRE(flags.value() == 0); + } + + SECTION ("construct from int") { + auto flags = testflags::from_int(one.value() | zero.value()); + REQUIRE(flags == (one | zero)); + } + + SECTION ("copy construct") { + auto flags = one; + auto flags2(flags); + REQUIRE(flags == flags2); + } +} + +TEST_CASE("flagset: operators", "[nihil]") +{ + SECTION ("operator|") { + REQUIRE((zero | one).value() == 0x3); + } + + SECTION ("operator|=") { + auto flags = zero; + flags |= one; + REQUIRE(flags.value() == 0x3); + } + + SECTION ("operator&") { + auto flags = zero | one; + + REQUIRE((flags & zero) == zero); + } + + SECTION ("operator&=") { + auto flags = zero | one | two; + REQUIRE(flags.value() == 0x7); + flags &= (zero | one); + REQUIRE(flags.value() == 0x3); + } + + SECTION ("operator^") { + auto flags = zero | one; + REQUIRE((flags ^ (one | two)) == (zero | two)); + } + + SECTION ("operator^=") { + auto flags = zero | one; + flags ^= (one | two); + REQUIRE(flags == (zero | two)); + } + + SECTION ("operator~") { + auto flags = ~zero; + REQUIRE(flags.value() == ~static_cast(1)); + } + + SECTION ("operator==") { + auto flags = zero; + REQUIRE(flags == zero); + REQUIRE(flags != one); + } +} + +TEST_CASE("flagset: assignment", "[nihil]") +{ + auto flags = zero; + REQUIRE(flags == zero); + + flags = one; + REQUIRE(flags == one); + REQUIRE(flags != zero); +} + +TEST_CASE("flagset: format", "[nihil]") +{ + REQUIRE(std::format("{}", testflags()) == "<>"); + REQUIRE(std::format("{}", zero) == "<0>"); + REQUIRE(std::format("{}", one) == "<1>"); + REQUIRE(std::format("{}", zero | one) == "<1,0>"); + + REQUIRE(std::format("{}", twelve) == "<12>"); + REQUIRE(std::format("{}", twelve | one) == "<12,1>"); +} + +TEST_CASE("flagset: ostream operator<<", "[nihil]") +{ + auto write = [](testflags flags) -> std::string { + auto strm = std::ostringstream(); + strm << flags; + return strm.str(); + }; + + REQUIRE(write(testflags()) == "<>"); + REQUIRE(write(zero) == "<0>"); + REQUIRE(write(one) == "<1>"); + REQUIRE(write(zero | one) == "<1,0>"); + + REQUIRE(write(twelve) == "<12>"); + REQUIRE(write(twelve | one) == "<12,1>"); +} +} // anonymous namespace diff --git a/nihil.util/nihil.util.ccm b/nihil.util/nihil.util.ccm index 1911ced..6b4bc30 100644 --- a/nihil.util/nihil.util.ccm +++ b/nihil.util/nihil.util.ccm @@ -4,6 +4,7 @@ export module nihil.util; export import :capture_stream; export import :construct; export import :ctype; +export import :flagset; export import :guard; export import :parse_size; export import :next_word; -- cgit v1.2.3