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)
 |