#pragma once /* This header helps to extend scoped enum types (enum class) in two possible ways: 1) Enabling bitwise operators for enums 2) Advanced bs_t<> template (this converts enum type to another "bitset" enum type) To enable bitwise operators, enum scope must contain `__bitwise_ops` entry. enum class flags { __bitwise_ops, // Not essential, but recommended to put it first flag1 = 1 << 0, flag2 = 1 << 1, }; Examples: `flags::flag1 | flags::flag2` - bitwise OR `flags::flag1 & flags::flag2` - bitwise AND `flags::flag1 ^ flags::flag2` - bitwise XOR `~flags::flag1` - bitwise NEG To enable bs_t<> template, enum scope must contain `__bitset_enum_max` entry. enum class flagzz : u32 { flag1, // Bit indices start from zero flag2, __bitset_enum_max // It must be the last value }; Now some operators are enabled for two enum types: `flagzz` and `bs_t`. These are very different from previously described bitwise operators. Examples: `+flagzz::flag1` - unary `+` operator convert flagzz value to bs_t `flagzz::flag1 + flagzz::flag2` - bitset union `flagzz::flag1 - flagzz::flag2` - bitset difference Intersection (&) and symmetric difference (^) is also available. */ #include "types.h" // Helper template template struct bs_base { // Underlying type using under = std::underlying_type_t; // Actual bitset type enum class type : under { null = 0, // Empty bitset __bitset_set_type = 0 // SFINAE marker }; static constexpr std::size_t bitmax = sizeof(T) * 8; static constexpr std::size_t bitsize = static_cast(T::__bitset_enum_max); static_assert(std::is_enum::value, "bs_t<> error: invalid type (must be enum)"); static_assert(!bitsize || bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max"); // Helper function static constexpr under shift(T value) { return static_cast(1) << static_cast(value); } friend type& operator +=(type& lhs, type rhs) { reinterpret_cast(lhs) |= static_cast(rhs); return lhs; } friend type& operator -=(type& lhs, type rhs) { reinterpret_cast(lhs) &= ~static_cast(rhs); return lhs; } friend type& operator &=(type& lhs, type rhs) { reinterpret_cast(lhs) &= static_cast(rhs); return lhs; } friend type& operator ^=(type& lhs, type rhs) { reinterpret_cast(lhs) ^= static_cast(rhs); return lhs; } friend type& operator +=(type& lhs, T rhs) { reinterpret_cast(lhs) |= shift(rhs); return lhs; } friend type& operator -=(type& lhs, T rhs) { reinterpret_cast(lhs) &= ~shift(rhs); return lhs; } friend type& operator &=(type& lhs, T rhs) { reinterpret_cast(lhs) &= shift(rhs); return lhs; } friend type& operator ^=(type& lhs, T rhs) { reinterpret_cast(lhs) ^= shift(rhs); return lhs; } friend constexpr type operator +(type lhs, type rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } friend constexpr type operator -(type lhs, type rhs) { return static_cast(static_cast(lhs) & ~static_cast(rhs)); } friend constexpr type operator &(type lhs, type rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } friend constexpr type operator ^(type lhs, type rhs) { return static_cast(static_cast(lhs) ^ static_cast(rhs)); } friend constexpr type operator &(type lhs, T rhs) { return static_cast(static_cast(lhs) & shift(rhs)); } friend constexpr type operator ^(type lhs, T rhs) { return static_cast(static_cast(lhs) ^ shift(rhs)); } friend constexpr type operator &(T lhs, type rhs) { return static_cast(shift(lhs) & static_cast(rhs)); } friend constexpr type operator ^(T lhs, type rhs) { return static_cast(shift(lhs) ^ static_cast(rhs)); } friend constexpr bool operator ==(T lhs, type rhs) { return shift(lhs) == rhs; } friend constexpr bool operator ==(type lhs, T rhs) { return lhs == shift(rhs); } friend constexpr bool operator !=(T lhs, type rhs) { return shift(lhs) != rhs; } friend constexpr bool operator !=(type lhs, T rhs) { return lhs != shift(rhs); } friend constexpr bool test(type value) { return static_cast(value) != 0; } friend constexpr bool test(type lhs, type rhs) { return (static_cast(lhs) & static_cast(rhs)) != 0; } friend constexpr bool test(type lhs, T rhs) { return (static_cast(lhs) & shift(rhs)) != 0; } friend constexpr bool test(T lhs, type rhs) { return (shift(lhs) & static_cast(rhs)) != 0; } friend bool test_and_set(type& lhs, type rhs) { return test_and_set(reinterpret_cast(lhs), static_cast(rhs)); } friend bool test_and_set(type& lhs, T rhs) { return test_and_set(reinterpret_cast(lhs), shift(rhs)); } friend bool test_and_reset(type& lhs, type rhs) { return test_and_reset(reinterpret_cast(lhs), static_cast(rhs)); } friend bool test_and_reset(type& lhs, T rhs) { return test_and_reset(reinterpret_cast(lhs), shift(rhs)); } friend bool test_and_complement(type& lhs, type rhs) { return test_and_complement(reinterpret_cast(lhs), static_cast(rhs)); } friend bool test_and_complement(type& lhs, T rhs) { return test_and_complement(reinterpret_cast(lhs), shift(rhs)); } }; // Bitset type for enum class with available bits [0, T::__bitset_enum_max) template using bs_t = typename bs_base::type; // Unary '+' operator: promote plain enum value to bitset value template constexpr bs_t operator +(T value) { return static_cast>(bs_base::shift(value)); } // Binary '+' operator: bitset union template constexpr bs_t operator +(T lhs, T rhs) { return static_cast>(bs_base::shift(lhs) | bs_base::shift(rhs)); } // Binary '+' operator: bitset union template constexpr bs_t operator +(typename bs_base::type lhs, T rhs) { return static_cast>(static_cast::under>(lhs) | bs_base::shift(rhs)); } // Binary '+' operator: bitset union template constexpr bs_t operator +(T lhs, typename bs_base::type rhs) { return static_cast>(bs_base::shift(lhs) | static_cast::under>(rhs)); } // Binary '-' operator: bitset difference template constexpr bs_t operator -(T lhs, T rhs) { return static_cast>(bs_base::shift(lhs) & ~bs_base::shift(rhs)); } // Binary '-' operator: bitset difference template constexpr bs_t operator -(typename bs_base::type lhs, T rhs) { return static_cast>(static_cast::under>(lhs) & ~bs_base::shift(rhs)); } // Binary '-' operator: bitset difference template constexpr bs_t operator -(T lhs, typename bs_base::type rhs) { return static_cast>(bs_base::shift(lhs) & ~static_cast::under>(rhs)); } template struct atomic_add>::value>>> { using under = typename bs_base::under; static inline bs_t op1(bs_t& left, T right) { return static_cast>(atomic_storage::fetch_or(reinterpret_cast(left), bs_base::shift(right))); } static constexpr auto fetch_op = &op1; static inline bs_t op2(bs_t& left, T right) { return static_cast>(atomic_storage::or_fetch(reinterpret_cast(left), bs_base::shift(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_sub>::value>>> { using under = typename bs_base::under; static inline bs_t op1(bs_t& left, T right) { return static_cast>(atomic_storage::fetch_and(reinterpret_cast(left), ~bs_base::shift(right))); } static constexpr auto fetch_op = &op1; static inline bs_t op2(bs_t& left, T right) { return static_cast>(atomic_storage::and_fetch(reinterpret_cast(left), ~bs_base::shift(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_and>::value>>> { using under = typename bs_base::under; static inline bs_t op1(bs_t& left, T right) { return static_cast>(atomic_storage::fetch_and(reinterpret_cast(left), bs_base::shift(right))); } static constexpr auto fetch_op = &op1; static inline bs_t op2(bs_t& left, T right) { return static_cast>(atomic_storage::and_fetch(reinterpret_cast(left), bs_base::shift(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_xor>::value>>> { using under = typename bs_base::under; static inline bs_t op1(bs_t& left, T right) { return static_cast>(atomic_storage::fetch_xor(reinterpret_cast(left), bs_base::shift(right))); } static constexpr auto fetch_op = &op1; static inline bs_t op2(bs_t& left, T right) { return static_cast>(atomic_storage::xor_fetch(reinterpret_cast(left), bs_base::shift(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_add> { using under = std::underlying_type_t; static inline T op1(T& left, T right) { return static_cast(atomic_storage::fetch_or(reinterpret_cast(left), static_cast(right))); } static constexpr auto fetch_op = &op1; static inline T op2(T& left, T right) { return static_cast(atomic_storage::or_fetch(reinterpret_cast(left), static_cast(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_sub> { using under = std::underlying_type_t; static inline T op1(T& left, T right) { return static_cast(atomic_storage::fetch_and(reinterpret_cast(left), ~static_cast(right))); } static constexpr auto fetch_op = &op1; static inline T op2(T& left, T right) { return static_cast(atomic_storage::and_fetch(reinterpret_cast(left), ~static_cast(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_and> { using under = std::underlying_type_t; static inline T op1(T& left, T right) { return static_cast(atomic_storage::fetch_and(reinterpret_cast(left), static_cast(right))); } static constexpr auto fetch_op = &op1; static inline T op2(T& left, T right) { return static_cast(atomic_storage::and_fetch(reinterpret_cast(left), static_cast(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_xor> { using under = std::underlying_type_t; static inline T op1(T& left, T right) { return static_cast(atomic_storage::fetch_xor(reinterpret_cast(left), static_cast(right))); } static constexpr auto fetch_op = &op1; static inline T op2(T& left, T right) { return static_cast(atomic_storage::xor_fetch(reinterpret_cast(left), static_cast(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_test_and_set>::value>>> { using under = typename bs_base::under; static inline bool _op(bs_t& left, T value) { return atomic_storage::bts(reinterpret_cast(left), static_cast(static_cast(value))); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_reset>::value>>> { using under = typename bs_base::under; static inline bool _op(bs_t& left, T value) { return atomic_storage::btr(reinterpret_cast(left), static_cast(static_cast(value))); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_complement>::value>>> { using under = typename bs_base::under; static inline bool _op(bs_t& left, T value) { return atomic_storage::btc(reinterpret_cast(left), static_cast(static_cast(value))); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_set> { using under = std::underlying_type_t; static inline bool _op(T& left, T value) { return atomic_storage::test_and_set(reinterpret_cast(left), static_cast(value)); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_reset> { using under = std::underlying_type_t; static inline bool _op(T& left, T value) { return atomic_storage::test_and_reset(reinterpret_cast(left), static_cast(value)); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_complement> { using under = std::underlying_type_t; static inline bool _op(T& left, T value) { return atomic_storage::test_and_complement(reinterpret_cast(left), static_cast(value)); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; // Binary '|' operator: bitwise OR template constexpr T operator |(T lhs, T rhs) { return static_cast(std::underlying_type_t(lhs) | std::underlying_type_t(rhs)); } // Binary '&' operator: bitwise AND template constexpr T operator &(T lhs, T rhs) { return static_cast(std::underlying_type_t(lhs) & std::underlying_type_t(rhs)); } // Binary '^' operator: bitwise XOR template constexpr T operator ^(T lhs, T rhs) { return static_cast(std::underlying_type_t(lhs) ^ std::underlying_type_t(rhs)); } // Unary '~' operator: bitwise NEG template constexpr T operator ~(T value) { return static_cast(~std::underlying_type_t(value)); } // Bitwise OR assignment template inline T& operator |=(T& lhs, T rhs) { reinterpret_cast&>(lhs) |= std::underlying_type_t(rhs); return lhs; } // Bitwise AND assignment template inline T& operator &=(T& lhs, T rhs) { reinterpret_cast&>(lhs) &= std::underlying_type_t(rhs); return lhs; } // Bitwise XOR assignment template inline T& operator ^=(T& lhs, T rhs) { reinterpret_cast&>(lhs) ^= std::underlying_type_t(rhs); return lhs; } template constexpr bool test(T value) { return std::underlying_type_t(value) != 0; } template constexpr bool test(T lhs, T rhs) { return (std::underlying_type_t(lhs) & std::underlying_type_t(rhs)) != 0; } template inline bool test_and_set(T& lhs, T rhs) { return test_and_set(reinterpret_cast&>(lhs), std::underlying_type_t(rhs)); } template inline bool test_and_reset(T& lhs, T rhs) { return test_and_reset(reinterpret_cast&>(lhs), std::underlying_type_t(rhs)); } template inline bool test_and_complement(T& lhs, T rhs) { return test_and_complement(reinterpret_cast&>(lhs), std::underlying_type_t(rhs)); } template struct atomic_or::value>>> { using under = std::underlying_type_t; static inline T op1(T& left, T right) { return static_cast(atomic_storage::fetch_or(reinterpret_cast(left), static_cast(right))); } static constexpr auto fetch_op = &op1; static inline T op2(T& left, T right) { return static_cast(atomic_storage::or_fetch(reinterpret_cast(left), static_cast(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_and::value>>> { using under = std::underlying_type_t; static inline T op1(T& left, T right) { return static_cast(atomic_storage::fetch_and(reinterpret_cast(left), static_cast(right))); } static constexpr auto fetch_op = &op1; static inline T op2(T& left, T right) { return static_cast(atomic_storage::and_fetch(reinterpret_cast(left), static_cast(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_xor::value>>> { using under = std::underlying_type_t; static inline T op1(T& left, T right) { return static_cast(atomic_storage::fetch_xor(reinterpret_cast(left), static_cast(right))); } static constexpr auto fetch_op = &op1; static inline T op2(T& left, T right) { return static_cast(atomic_storage::xor_fetch(reinterpret_cast(left), static_cast(right))); } static constexpr auto op_fetch = &op2; static constexpr auto atomic_op = &op2; }; template struct atomic_test_and_set::value>>> { using under = std::underlying_type_t; static inline bool _op(T& left, T value) { return atomic_storage::test_and_set(reinterpret_cast(left), static_cast(value)); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_reset::value>>> { using under = std::underlying_type_t; static inline bool _op(T& left, T value) { return atomic_storage::test_and_reset(reinterpret_cast(left), static_cast(value)); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; }; template struct atomic_test_and_complement::value>>> { using under = std::underlying_type_t; static inline bool _op(T& left, T value) { return atomic_storage::test_and_complement(reinterpret_cast(left), static_cast(value)); } static constexpr auto fetch_op = &_op; static constexpr auto op_fetch = &_op; static constexpr auto atomic_op = &_op; };