#pragma once #if !defined(MIJIN_UTIL_BITFLAGS_HPP_INCLUDED) #define MIJIN_UTIL_BITFLAGS_HPP_INCLUDED 1 #include #include #include #include "../util/traits.hpp" namespace mijin { // // public defines // // // public constants // // // public types // template struct BitFlags { using bits_t = TBits; static constexpr bool ENABLE_TO_INT = false; constexpr TBits& operator |=(const BitFlags& other) { for (std::size_t idx = 0; idx < sizeof(TBits); ++idx) { *(std::bit_cast(asBits()) + idx) |= *(std::bit_cast(other.asBits()) + idx); } return *asBits(); } constexpr TBits& operator &=(const BitFlags& other) { for (std::size_t idx = 0; idx < sizeof(TBits); ++idx) { *(std::bit_cast(asBits()) + idx) &= *(std::bit_cast(other.asBits()) + idx); } return *asBits(); } constexpr TBits& operator ^=(const BitFlags& other) { for (std::size_t idx = 0; idx < sizeof(TBits); ++idx) { *(std::bit_cast(asBits()) + idx) ^= *(std::bit_cast(other.asBits()) + idx); } return *asBits(); } constexpr TBits operator& (const BitFlags& other) const { TBits copy(*asBits()); copy &= other; return copy; } constexpr TBits operator| (const BitFlags& other) const { TBits copy(*asBits()); copy |= other; return copy; } constexpr TBits operator^ (const BitFlags& other) const { TBits copy(*asBits()); copy ^= other; return copy; } explicit constexpr operator bool() const { for (std::size_t idx = 0; idx < sizeof(TBits); ++idx) { if (*(std::bit_cast(asBits()) + idx) != std::byte(0)) { return true; } } return false; } constexpr bool operator!() const { return !static_cast(*this); } auto operator<=>(const BitFlags&) const noexcept = default; private: constexpr TBits* asBits() { return static_cast(this); } constexpr const TBits* asBits() const { return static_cast(this); } }; template constexpr bool is_bitflags_v = std::is_base_of_v, T>; template concept BitFlagsType = is_bitflags_v; // // public functions // template TInt bitFlagsToInt(const BitFlags& flags) noexcept { // If a BitFlags object contains padding (number of defined bits < number of bits in type), the bits in the padding might not be 0. // In that case bit_casting will produce an incorrect value. // Filling the gaps with padding bits fixes this, but is unfortunately quite error prone :/. static_assert(T::ENABLE_TO_INT, "bitFlagsToInt not enabled for this type. Make sure the number of bits defined is the same as the number of bits in the type and define ENABLE_TO_INT to true."); static constexpr std::size_t BYTES = std::min(sizeof(T), sizeof(TInt)); TInt result = 0; for (std::size_t off = 0; off < BYTES; ++off) { result |= static_cast(*(std::bit_cast(&flags) + off)) << (off * 8); } return result; } template T bitFlagsFromInt(TInt intVal) noexcept { static constexpr std::size_t BYTES = std::min(sizeof(T), sizeof(TInt)); T result = {}; for (std::size_t off = 0; off < BYTES; ++off) { (*(std::bit_cast(&result) + off)) = static_cast(intVal >> (off * 8)); } return result; } } // namespace mijin #endif // !defined(MIJIN_UTIL_BITFLAGS_HPP_INCLUDED)