#pragma once #include "types.h" // Small bitset for enum class types with available values [0, BitSize). // T must be either enum type or convertible to (registered with via simple_t). // Internal representation is single value of type T. template struct bitset_t { using type = simple_t; using under = std::underlying_type_t; static constexpr auto bitsize = BitSize; bitset_t() = default; constexpr bitset_t(type _enum_const) : m_value(static_cast(shift(_enum_const))) { } constexpr bitset_t(under raw_value, const std::nothrow_t&) : m_value(static_cast(raw_value)) { } // Get underlying value constexpr under _value() const { return static_cast(m_value); } explicit constexpr operator bool() const { return _value() ? true : false; } bitset_t& operator +=(bitset_t rhs) { return *this = { _value() | rhs._value(), std::nothrow }; } bitset_t& operator -=(bitset_t rhs) { return *this = { _value() & ~rhs._value(), std::nothrow }; } bitset_t& operator &=(bitset_t rhs) { return *this = { _value() & rhs._value(), std::nothrow }; } bitset_t& operator ^=(bitset_t rhs) { return *this = { _value() ^ rhs._value(), std::nothrow }; } friend constexpr bitset_t operator +(bitset_t lhs, bitset_t rhs) { return{ lhs._value() | rhs._value(), std::nothrow }; } friend constexpr bitset_t operator -(bitset_t lhs, bitset_t rhs) { return{ lhs._value() & ~rhs._value(), std::nothrow }; } friend constexpr bitset_t operator &(bitset_t lhs, bitset_t rhs) { return{ lhs._value() & rhs._value(), std::nothrow }; } friend constexpr bitset_t operator ^(bitset_t lhs, bitset_t rhs) { return{ lhs._value() ^ rhs._value(), std::nothrow }; } bool test(bitset_t rhs) const { const under v = _value(); const under s = rhs._value(); return (v & s) != 0; } bool test_and_set(bitset_t rhs) { const under v = _value(); const under s = rhs._value(); *this = { v | s, std::nothrow }; return (v & s) != 0; } bool test_and_reset(bitset_t rhs) { const under v = _value(); const under s = rhs._value(); *this = { v & ~s, std::nothrow }; return (v & s) != 0; } bool test_and_complement(bitset_t rhs) { const under v = _value(); const under s = rhs._value(); *this = { v ^ s, std::nothrow }; return (v & s) != 0; } private: static constexpr under shift(const T& value) { return static_cast(value) < BitSize ? static_cast(1) << static_cast(value) : throw value; } T m_value; }; template constexpr RT make_bitset() { return RT{}; } // Fold enum constants into bitset_t<> (must be implemented with constexpr initializer_list constructor instead) template::value, bitset_t, T>> constexpr RT make_bitset(Arg&& _enum_const, Args&&... args) { return RT{ std::forward(_enum_const) } + make_bitset(std::forward(args)...); } template struct atomic_add, CT, std::enable_if_t::value>> { using under = typename bitset_t::under; static inline bitset_t op1(bitset_t& left, bitset_t right) { return{ atomic_storage::fetch_or(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static inline bitset_t op2(bitset_t& left, bitset_t right) { return{ atomic_storage::or_fetch(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_sub, CT, std::enable_if_t::value>> { using under = typename bitset_t::under; static inline bitset_t op1(bitset_t& left, bitset_t right) { return{ atomic_storage::fetch_and(reinterpret_cast(left), ~right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static inline bitset_t op2(bitset_t& left, bitset_t right) { return{ atomic_storage::and_fetch(reinterpret_cast(left), ~right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_and, CT, std::enable_if_t::value>> { using under = typename bitset_t::under; static inline bitset_t op1(bitset_t& left, bitset_t right) { return{ atomic_storage::fetch_and(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static inline bitset_t op2(bitset_t& left, bitset_t right) { return{ atomic_storage::and_fetch(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_xor, CT, std::enable_if_t::value>> { using under = typename bitset_t::under; static inline bitset_t op1(bitset_t& left, bitset_t right) { return{ atomic_storage::fetch_xor(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto fetch_op = &op1; static inline bitset_t op2(bitset_t& left, bitset_t right) { return{ atomic_storage::xor_fetch(reinterpret_cast(left), right._value()), std::nothrow }; } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_test_and_set, T, std::enable_if_t::value>> { using under = typename bitset_t::under; static inline bool _op(bitset_t& left, const T& value) { return atomic_storage::bts(reinterpret_cast(left), static_cast(value)); } static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_reset, T, std::enable_if_t::value>> { using under = typename bitset_t::under; static inline bool _op(bitset_t& left, const T& value) { return atomic_storage::btr(reinterpret_cast(left), static_cast(value)); } static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_complement, T, std::enable_if_t::value>> { using under = typename bitset_t::under; static inline bool _op(bitset_t& left, const T& value) { return atomic_storage::btc(reinterpret_cast(left), static_cast(value)); } static constexpr auto atomic_op = &_op; };