diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-07-02 04:00:06 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-07-02 04:00:06 +0100 |
| commit | 5adeb648f74c1771164c0686d6e0fc584cf36d9e (patch) | |
| tree | 060cd918d3dd9e931a1541a43c9edff1a404ff47 /nihil.core/flagset.ccm | |
| parent | 06fafff8e9e9c096cc39bde0306caa53ad3a2351 (diff) | |
| download | nihil-5adeb648f74c1771164c0686d6e0fc584cf36d9e.tar.gz nihil-5adeb648f74c1771164c0686d6e0fc584cf36d9e.tar.bz2 | |
move everything from util to core
Diffstat (limited to 'nihil.core/flagset.ccm')
| -rw-r--r-- | nihil.core/flagset.ccm | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/nihil.core/flagset.ccm b/nihil.core/flagset.ccm new file mode 100644 index 0000000..cea9889 --- /dev/null +++ b/nihil.core/flagset.ccm @@ -0,0 +1,200 @@ +// This source code is released into the public domain. +export module nihil.core:flagset; + +/* + * flagset: a type-safe flags type. + */ + +import nihil.std; + +namespace nihil { + +export template <std::integral base_type, typename Tag> +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 <base_type flag> + [[nodiscard]] static constexpr auto mask() noexcept -> flagset + { + return flagset(flag); + } + + /* + * Create flags for a specific bit. + */ + template <unsigned bitnr> + [[nodiscard]] static constexpr auto bit() noexcept -> flagset + { + static_assert(bitnr < std::numeric_limits<base_type>::digits); + return flagset(static_cast<base_type>(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<base_type, Tag> flags) -> std::ostream & + { + std::print(strm, "{}", flags); + return strm; + } +}; + +} // namespace nihil + +/* + * Formatting for flagset. + */ +export template <std::integral base_type, typename Tag, typename Char> +struct std::formatter<nihil::flagset<base_type, Tag>, Char> +{ + using flags_t = nihil::flagset<base_type, Tag>; + + template <typename ParseContext> + constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator + { + return ctx.begin(); + } + + template <typename FmtContext> + auto format(flags_t flags, FmtContext &ctx) const -> FmtContext::iterator + { + auto constexpr digits = std::numeric_limits<base_type>::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<base_type>(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; + } +}; |
