aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.util/flagset.ccm
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.util/flagset.ccm')
-rw-r--r--nihil.util/flagset.ccm200
1 files changed, 200 insertions, 0 deletions
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 <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;
+ }
+};