创建单个掩码以检查位是否已设置、未设置或接受

Create a single mask to check if bits are set, unset or accept either

给定一个值,我想检查它的一些位是否已设置,同时是否有一些未设置,而我不关心其他值。

我可以用两个位掩码来做到这一点,一个用于设置位,一个用于未设置位,如下所示:

#include <iostream>

bool checkMask(uint8_t value, uint8_t setmask, uint8_t unsetmask)
{
    return (value & setmask) == setmask && (~value & ~unsetmask) == ~unsetmask;
}

int main() {
    uint8_t setmask = 0b0100'0001;
    uint8_t unsetmask = 0b1111'1011;
    
    uint8_t valueMatches = 0b01101'1011;
    uint8_t valueFails1 = 0b00101'1010;
    uint8_t valueFails2 = 0b01101'1111;
    std::cout << "matches " <<  checkMask(valueMatches, setmask, unsetmask) << std::endl;
    std::cout << "fails1 " <<  checkMask(valueFails1, setmask, unsetmask) << std::endl;
    std::cout << "fails2 " <<  checkMask(valueFails2, setmask, unsetmask) << std::endl;
}

(可能有bug,举个例子)

当然,这也可以用字符串来完成,在这里我可以用通配符值来表示多个 0 和 1,例如:

string bitmask = ".1...001";

然后从字符串中逐位检查,忽略'.'并检查 0 和 1 是否匹配。

在我的解决方案中有一个权衡,要么使用 2 个值,这使得它不那么直观(特别是未设置的掩码),要么使用一个效率更低但非常清晰的字符串。

还有其他选择吗?

一些基于@KamilCuk 评论的大多数通用可能性

#include <iostream>
#include <utility>
#include <array>
#include <bitset>
#include <string>

constexpr uint8_t BYTE_BITS = 8;

template <typename D>
class BitMask
{
    public:
    constexpr BitMask(const D setmask, const D unsetmask) : 
        m_setmask(setmask), m_unsetmask(unsetmask) 
    {}

    constexpr BitMask(const char * bits)
    {
        m_setmask = 0;
        m_unsetmask = 0;

        for (int i = 0; i < sizeof(D)*BYTE_BITS; i++)
        {
            m_setmask = m_setmask << 1;
            m_unsetmask = m_unsetmask << 1;
            m_setmask += bits[i] == '1' ? 1 : 0;
            m_unsetmask += bits[i] == '0' ? 0 : 1;
        }
    }

    constexpr BitMask(const std::array<D,sizeof(D)*BYTE_BITS>& bits)
    {
        m_setmask = 0;
        m_unsetmask = 0;

        for (int i = 0; i < sizeof(D)*BYTE_BITS; i++)
        {
            m_setmask = m_setmask << 1;
            m_unsetmask = m_unsetmask << 1;
            m_setmask += bits[i] == 1 ? 1 : 0;
            m_unsetmask += bits[i] == 0 ? 0 : 1;
        }
    }

    constexpr bool check(const D value) const 
    {
        return BitMask::check(value, m_setmask, m_unsetmask);
    }

    static bool check(const D value, const D setmask, const D unsetmask)
    {
        return (value & setmask) == setmask && (~value & ~unsetmask) == ~unsetmask;
    }

    void print() const
    {
        std::cout << "Set mask: " << std::bitset<sizeof(D)*BYTE_BITS>(m_setmask) << '\n'
                  << "Unset mask: " << std::bitset<sizeof(D)*BYTE_BITS>(m_unsetmask) << '\n';
    }

    private:
    D m_setmask;
    D m_unsetmask;
};


int main() {
    constexpr BitMask<uint8_t> mask1(0b0100'0001, 0b1111'1011);
    constexpr BitMask<uint8_t> mask2(std::array<uint8_t, sizeof(uint8_t)*BYTE_BITS>{2,1,2,2,2,0,2,1});
    constexpr BitMask<uint8_t> mask3("?1???0?1");
        
    uint8_t valueMatches = 0b01101'1011;
    uint8_t valueFails1 = 0b00101'1010;
    uint8_t valueFails2 = 0b01101'1111;
    std::cout << "matches " <<  mask1.check(valueMatches) << std::endl;
    std::cout << "fails1 " <<  mask1.check(valueFails1) << std::endl;
    std::cout << "fails2 " <<  mask1.check(valueFails2) << std::endl;

    mask1.print();
    mask2.print();
    mask3.print();

    return 0;
}

可能字符串选项没有我最初想象的那么慢。至少考虑到它的使用是多么直观。

问题包括不检查字符串长度。在 constexpr 中,如果字符串太短,它就不会编译,这很好,但如果它更长,它将被忽略。无法在 constexpr 上下文中创建 std::string 来检查大小是否与类型匹配,尽管 strlen 应该是 constexpr。

根据使用此代码的用例,您也可以尝试以下操作:

bool check(uint8_t value, uint8_t pattern, uint8_t relevant_bits) {
  return (relevant_bits & pattern) == (relevant_bits & (value ^ ~pattern));
}

如果您为位命名掩码(例如在控制寄存器中)类似于:

constexpr uint8_t F1 = 0x01;
...
constexpr uint8_t F8 = 0x80;

你可以像这样使用这个函数:

  if (check(value, F1|F4|F6, F1|F2|F3|F4|F6)) {
     std::cout << "match!" << std::endl;
  } else {
     std::cout << "not a match!" << std::endl;
  }

pattern 隐式列出了想要的未设置位,而 relevant_bits 仍然显示,它们很好......相关。

您可以判断这种处理方式是否更适合您的用例。