创建单个掩码以检查位是否已设置、未设置或接受
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
仍然显示,它们很好......相关。
您可以判断这种处理方式是否更适合您的用例。
给定一个值,我想检查它的一些位是否已设置,同时是否有一些未设置,而我不关心其他值。
我可以用两个位掩码来做到这一点,一个用于设置位,一个用于未设置位,如下所示:
#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
仍然显示,它们很好......相关。
您可以判断这种处理方式是否更适合您的用例。