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

问题描述

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

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

#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,unsetmask) << std::endl;
    std::cout << "fails2 " <<  checkMask(valueFails2,unsetmask) << std::endl;
}

(可能有问题,举个例子)

当然,这也可以用字符串来完成,在这里我可以用通配符值表示 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,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 仍然显示,它们很好......相关。

这种处理方式是否对您的用例更方便,您自己判断。