rpcsx/rx/include/rx/EnumBitSet.hpp

349 lines
9.9 KiB
C++
Raw Normal View History

2025-10-04 21:19:57 +02:00
#pragma once
/*
This header implements EnumBitSet<> class for scoped enum types (enum class).
To enable EnumBitSet<>, enum scope must contain `bitset_last` entry.
enum class flagzz : u32
{
flag1, // Bit indices start from zero
flag2,
bitset_last = flag2
};
This also enables helper operators for this enum type.
Examples:
`flagzz::flag1 | flagzz::flag2` - bitset union
`flagzz::flag1 & ~flagzz::flag2` - bitset difference
Intersection (&) and symmetric difference (^) is also available.
*/
#include "format-base.hpp"
#include "rx/refl.hpp"
#include <type_traits>
2025-10-04 21:19:57 +02:00
namespace rx {
template <typename T>
concept BitSetEnum = std::is_enum_v<T> && requires { T::bitset_last; };
template <BitSetEnum T> class EnumBitSet;
namespace detail {
template <BitSetEnum T> class InvertedEnumBitSet final {
using underlying_type = std::underlying_type_t<T>;
underlying_type m_data;
constexpr InvertedEnumBitSet(underlying_type data) : m_data(data) {}
friend EnumBitSet<T>;
};
} // namespace detail
// Bitset type for enum class with available bits [0, fieldCount)
template <BitSetEnum T> class EnumBitSet final {
public:
// Underlying type
using underlying_type = std::underlying_type_t<T>;
private:
// Underlying value
underlying_type m_data;
// Value constructor
constexpr explicit EnumBitSet(int, underlying_type data) noexcept
: m_data(data) {}
public:
static constexpr std::size_t bitmax = sizeof(T) * 8;
static constexpr std::size_t bitsize =
2025-10-04 21:19:57 +02:00
static_cast<underlying_type>(T::bitset_last) + 1;
static_assert(std::is_enum_v<T>,
"BitSet<> error: invalid type (must be enum)");
static_assert(bitsize <= bitmax,
"BitSet<> error: failed to determine enum field count");
static_assert(bitsize != bitmax || std::is_unsigned_v<underlying_type>,
"BitSet<> error: invalid field count (sign bit)");
// Helper function
static constexpr underlying_type shift(T value) {
return static_cast<underlying_type>(1)
<< static_cast<underlying_type>(value);
}
constexpr EnumBitSet() = default;
2025-10-04 21:19:57 +02:00
// Construct from a single bit
constexpr EnumBitSet(T bit) noexcept : m_data(shift(bit)) {}
[[nodiscard]] constexpr underlying_type toUnderlying() const {
return m_data;
}
[[nodiscard]] static constexpr EnumBitSet
fromUnderlying(underlying_type raw) {
return EnumBitSet(0, raw);
}
// Test for empty bitset
constexpr explicit operator bool() const noexcept { return m_data != 0; }
// Extract underlying data
constexpr explicit operator underlying_type() const noexcept {
return m_data;
}
constexpr detail::InvertedEnumBitSet<T> operator~() const {
return static_cast<underlying_type>(~m_data);
}
2025-10-04 21:19:57 +02:00
[[deprecated("Use operator|=")]] constexpr EnumBitSet &
operator+=(EnumBitSet rhs) {
m_data |= static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet &operator|=(EnumBitSet rhs) {
m_data |= static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet &operator-=(EnumBitSet rhs) {
m_data &= ~static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet without(EnumBitSet rhs) const {
EnumBitSet result = *this;
result.m_data &= ~static_cast<underlying_type>(rhs);
return result;
}
constexpr EnumBitSet with(EnumBitSet rhs) const {
EnumBitSet result = *this;
result.m_data |= static_cast<underlying_type>(rhs);
return result;
}
constexpr EnumBitSet &operator&=(EnumBitSet rhs) {
m_data &= static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet &operator&=(detail::InvertedEnumBitSet<T> rhs) {
m_data &= rhs.m_data;
return *this;
}
2025-10-04 21:19:57 +02:00
constexpr EnumBitSet &operator^=(EnumBitSet rhs) {
m_data ^= static_cast<underlying_type>(rhs);
return *this;
}
[[deprecated("Use operator|")]] friend constexpr EnumBitSet
operator+(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data | rhs.m_data);
}
friend constexpr EnumBitSet operator-(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data & ~rhs.m_data);
}
friend constexpr EnumBitSet operator|(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data | rhs.m_data);
}
friend constexpr EnumBitSet operator&(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data & rhs.m_data);
}
friend constexpr EnumBitSet operator&(EnumBitSet lhs,
detail::InvertedEnumBitSet<T> rhs) {
return EnumBitSet(0, lhs.m_data & rhs.m_data);
}
friend constexpr EnumBitSet operator^(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data ^ rhs.m_data);
}
constexpr bool operator==(EnumBitSet rhs) const noexcept {
return m_data == rhs.m_data;
}
constexpr bool test_and_set(T bit) {
bool r = (m_data & shift(bit)) != 0;
m_data |= shift(bit);
return r;
}
constexpr bool test_and_reset(T bit) {
bool r = (m_data & shift(bit)) != 0;
m_data &= ~shift(bit);
return r;
}
constexpr bool test_and_complement(T bit) {
bool r = (m_data & shift(bit)) != 0;
m_data ^= shift(bit);
return r;
}
constexpr bool any_of(EnumBitSet arg) const {
return (m_data & arg.m_data) != 0;
}
constexpr bool all_of(EnumBitSet arg) const {
return (m_data & arg.m_data) == arg.m_data;
}
constexpr bool none_of(EnumBitSet arg) const {
return (m_data & arg.m_data) == 0;
}
constexpr underlying_type &raw() { return m_data; }
constexpr auto operator<=>(const EnumBitSet &) const = default;
2025-10-04 21:19:57 +02:00
};
template <BitSetEnum T> constexpr EnumBitSet<T> toBitSet(T bit) {
return EnumBitSet<T>(bit);
}
namespace bitset {
// Unary '+' operator: promote plain enum value to bitset value
template <BitSetEnum T>
[[deprecated("Use toBitSet(bit)")]] constexpr EnumBitSet<T> operator+(T bit) {
return EnumBitSet<T>(bit);
}
2025-10-16 10:42:06 +02:00
template <BitSetEnum T>
constexpr detail::InvertedEnumBitSet<T> operator~(T bit) {
return ~toBitSet(bit);
}
2025-10-04 21:19:57 +02:00
// Binary '+' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
[[deprecated("Use operator|")]] constexpr EnumBitSet<T>
operator+(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '+' operator: bitset union
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
[[deprecated("Use operator|")]] constexpr EnumBitSet<T> operator+(const U &lhs,
T rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '|' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator|(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '|' operator: bitset union
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator|(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '-' operator: bitset difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator-(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) - EnumBitSet<T>(rhs);
}
// Binary '-' operator: bitset difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator-(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) - EnumBitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator&(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) & EnumBitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator&(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) & EnumBitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <BitSetEnum T, typename U>
constexpr EnumBitSet<T> operator&(T lhs, detail::InvertedEnumBitSet<T> rhs) {
return EnumBitSet<T>(lhs) & rhs;
}
// Binary '^' operator: bitset symmetric difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator^(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) ^ EnumBitSet<T>(rhs);
}
// Binary '^' operator: bitset symmetric difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator^(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) ^ EnumBitSet<T>(rhs);
}
} // namespace bitset
} // namespace rx
template <typename T>
requires requires(rx::format_parse_context &ctx) {
rx::formatter<T>().parse(ctx);
}
struct rx::formatter<rx::EnumBitSet<T>> {
constexpr rx::format_parse_context::iterator
parse(rx::format_parse_context &ctx) {
return ctx.begin();
}
rx::format_context::iterator format(rx::EnumBitSet<T> bitSet,
rx::format_context &ctx) const {
auto raw = bitSet.toUnderlying();
if (raw != 0) {
bool first = true;
for (std::size_t i = 0; i <= static_cast<std::size_t>(T::bitset_last);
++i) {
auto mask = 1ull << i;
if (!(raw & mask)) {
continue;
}
if (first) {
first = false;
} else {
rx::format_to(ctx.out(), " | ");
}
rx::format_to(ctx.out(), "{}", T(i));
raw &= ~mask;
}
if (raw) {
if (!first) {
rx::format_to(ctx.out(), " | ");
}
rx::format_to(ctx.out(), "{:#x}", raw);
}
} else {
rx::format_to(ctx.out(), "{}::None", rx::getNameOf<T>());
}
return ctx.out();
}
};
2025-10-04 21:19:57 +02:00
using namespace rx::bitset;