139 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			139 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_UTIL_BITFLAGS_HPP_INCLUDED)
 | |
| #define MIJIN_UTIL_BITFLAGS_HPP_INCLUDED 1
 | |
| 
 | |
| #include <bit>
 | |
| #include <compare>
 | |
| #include <cstddef>
 | |
| 
 | |
| #include "../util/traits.hpp"
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| 
 | |
| //
 | |
| // public defines
 | |
| //
 | |
| 
 | |
| //
 | |
| // public constants
 | |
| //
 | |
| 
 | |
| //
 | |
| // public types
 | |
| //
 | |
| 
 | |
| template<typename TBits>
 | |
| 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<std::byte*>(asBits()) + idx) |= *(std::bit_cast<const std::byte*>(other.asBits()) + idx);
 | |
|         }
 | |
|         return *asBits();
 | |
|     }
 | |
|     constexpr TBits& operator &=(const BitFlags& other) {
 | |
|         for (std::size_t idx = 0; idx < sizeof(TBits); ++idx) {
 | |
|             *(std::bit_cast<std::byte*>(asBits()) + idx) &= *(std::bit_cast<const std::byte*>(other.asBits()) + idx);
 | |
|         }
 | |
|         return *asBits();
 | |
|     }
 | |
| 
 | |
|     constexpr TBits& operator ^=(const BitFlags& other) {
 | |
|         for (std::size_t idx = 0; idx < sizeof(TBits); ++idx) {
 | |
|             *(std::bit_cast<std::byte*>(asBits()) + idx) ^= *(std::bit_cast<const std::byte*>(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<const std::byte*>(asBits()) + idx) != std::byte(0)) {
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     constexpr bool operator!() const {
 | |
|         return !static_cast<bool>(*this);
 | |
|     }
 | |
| 
 | |
|     auto operator<=>(const BitFlags&) const noexcept = default;
 | |
| private:
 | |
|     constexpr TBits* asBits() { return static_cast<TBits*>(this); }
 | |
|     constexpr const TBits* asBits() const { return static_cast<const TBits*>(this); }
 | |
| };
 | |
| 
 | |
| template<typename T>
 | |
| constexpr bool is_bitflags_v = std::is_base_of_v<BitFlags<T>, T>;
 | |
| 
 | |
| template<typename T>
 | |
| concept BitFlagsType = is_bitflags_v<T>;
 | |
| 
 | |
| //
 | |
| // public functions
 | |
| //
 | |
| 
 | |
| template<std::integral TInt, BitFlagsType T>
 | |
| TInt bitFlagsToInt(const BitFlags<T>& 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<TInt>(*(std::bit_cast<const std::byte*>(&flags) + off)) << (off * 8);
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| template<BitFlagsType T, std::integral TInt>
 | |
| 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<std::byte*>(&result) + off)) = static_cast<std::byte>(intVal >> (off * 8));
 | |
|     }
 | |
| 
 | |
|     return result;
 | |
| }
 | |
| } // namespace mijin
 | |
| 
 | |
| #endif // !defined(MIJIN_UTIL_BITFLAGS_HPP_INCLUDED)
 | 
