#ifndef UTILS_BITFIELD_H #define UTILS_BITFIELD_H #include #include namespace bitfield { namespace impl { /** * Constant check for supported underlying BITFILED types. */ template static constexpr bool is_bitfield_type_v = std::is_integral::value && std::is_unsigned::value && !std::is_same::value; /** * Compute a BITMASK based on the HIGHBIT and LOWBIT template parameters. * The HIGHBIT and LOWBIT are inclusive. * * # Example * ComputeMask<4, 7, uint32_t>() -> 0xf0 */ template static constexpr inline ValueType compute_mask() { using unsigned_t = typename std::make_unsigned::type; constexpr unsigned_t kMaxBits = sizeof(unsigned_t) * 8; static_assert(HighBit < kMaxBits, "HighBit exceeds bits of ValueType"); static_assert(LowBit <= HighBit, "HighBit must not be larger than LowBit"); constexpr unsigned_t kLen = HighBit - LowBit; return kLen == (kMaxBits - 1) ? ~static_cast(0) : ((static_cast(1) << (HighBit - LowBit + 1)) - 1) << LowBit; } } // namespace impl // -- FIELD REF ---------------------------------------------------------------- /** * A mutable reference to a single FIELD in a BITFIELD. */ template struct field_ref { constexpr explicit field_ref(ValueType& val) : m_val{val} {} constexpr operator ValueType() const { return (m_val & kMask) >> LowBit; } constexpr ValueType val() const { return static_cast(*this); } constexpr field_ref& operator=(ValueType val) { m_val &= ~kMask; return operator|=(val); } constexpr field_ref& operator|=(ValueType val) { m_val |= (val << LowBit) & kMask; return *this; } constexpr field_ref& operator&=(ValueType val) { m_val &= (val << LowBit) & kMask; return *this; } private: ValueType& m_val; enum : ValueType { kMask = impl::compute_mask(), }; }; // -- CONST FIELD REF ---------------------------------------------------------- /** * A constant reference to a single FIELD in a BITFIELD. */ template struct const_field_ref { constexpr explicit const_field_ref(const ValueType& val) : m_val{val} {} constexpr operator ValueType() const { return (m_val & kMask) >> LowBit; } constexpr ValueType val() const { return static_cast(*this); } private: const ValueType& m_val; enum : ValueType { kMask = impl::compute_mask(), }; }; // -- BITFIELD ----------------------------------------------------------------- /** * The BITFIELD base class. */ template struct bitfield { static_assert(impl::is_bitfield_type_v, "bitfield instantiated with incorrect type"); constexpr explicit bitfield(ValueType val) : m_val{val} {} constexpr operator ValueType() const { return m_val; } constexpr ValueType val() const { return static_cast(*this); } #define OPERATOR(OP) \ constexpr bitfield& operator OP(ValueType val) { \ m_val OP val; \ return *this; \ } OPERATOR(=) OPERATOR(|=) OPERATOR(&=) #undef OPERATOR protected: ValueType m_val; }; // -- MACROS ------------------------------------------------------------------- // // Code generator macros to conveniently define BITFIELDs with multiple FIELDs. // // # Example // // BITFIELD_START(status_reg, std::uint32_t) // FIELD(N, 0, 0) // FIELD(V, 1, 1) // FIELD(C, 2, 2) // FIELD(Z, 3, 3) // FIELD(MODE, 4, 7) // BITFIELD_END() // // status_ref r(0); // // std::uint32_t n = r.V(); // r.V() = 0xfff; // assert(r.V() == 0x1); // assert(r == 0x2); #define BITFIELD_START(NAME, TYPE) \ struct NAME : public bitfield::bitfield { \ using ValueType = TYPE; \ using bitfield::operator=; \ \ constexpr explicit NAME(ValueType val) : bitfield{val} {} #define BITFIELD_END() \ } \ ; #define FIELD(NAME, L, H) \ constexpr ::bitfield::field_ref NAME() { \ return ::bitfield::field_ref(m_val); \ } \ \ constexpr ::bitfield::const_field_ref NAME() const { \ return ::bitfield::const_field_ref(m_val); \ } // -- TESTS -------------------------------------------------------------------- static_assert(impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(impl::compute_mask<0, 0, std::uint32_t>() == 0x1, ""); static_assert(impl::compute_mask<4, 7, std::uint32_t>() == 0xf0, ""); static_assert(impl::compute_mask<12, 12, std::uint32_t>() == 0x1000, ""); static_assert(impl::compute_mask<15, 16, std::uint32_t>() == 0x18000, ""); static_assert(impl::compute_mask<0, 31, std::uint32_t>() == 0xffffffff, ""); } // namespace bitfield #endif