以 std::bitset 的身份访问 std::uint8_t
Accessing std::uint8_t as std::bitset
我想在 C/C++ 中模拟一个状态寄存器,它应该可以作为 std::bitset and as std::uint8_t 访问。因此,我将它们组合为联合,如下所示:
#include <bitset>
#include <iostream>
union byte {
std::uint8_t uint;
std::bitset<8> bitset;
};
int main(int, char*[])
{
byte b = { .uint = 0b10101010 };
std::cout << "Value of bit 5: "
<< (b.bitset.test(5) ? "true" : "false") << std::endl;
std::cout << "Value of bit 4: "
<< (b.bitset.test(4) ? "true" : "false") << std::endl;
std::cout << "Bitset Output: " << b.bitset << std::endl;
std::cout << "Uint Output: " << static_cast<int>(b.uint) << std::endl;
return 0;
}
当使用 GCC x86_64 8.2 编译时,这似乎按预期工作。但是,我想知道我是否可以期望它在所有情况下都能正常工作,或者我是否可以使用 bitset
、bittest
、...
等辅助函数更好
你在这里尝试用 union
做的事情叫做类型双关,是 C++ 中的未定义行为(你可以阅读更多关于它的信息 in this SO answer),所以它不能保证甚至可以正常工作在同一个编译器上。
此外,即使允许,std::bitset<8>
也不能保证与 std::uint8_t
具有相同的表示形式(事实上,它在任何主要编译器上都没有)。
在您的情况下,您可以使用常规 std::bitset<8>
和 to_ulong
方法。
另一种选择是使用带有 bitset 成员的包装器 class,它会提供从 assign/convert 到 uint8_t
的便捷方法。
此外,如果您只想要 std::bitset<8>
中有限的 API,这可能是个好主意(如果您想将 class 的大小保持为 1 个字节)环绕 std::uint8_t
并手动实现这几个方法(如 test
)。
我采纳了 SomeProgrammerDude 的评论
As for your problem, if you want a type that can act as both a native uint8_t and handle bits in a "nice" way, then you have to implement such a class yourself. If you need it to map to a memory-mapped hardware register it should probably wrap a pointer to the register.
并尝试用 C++ 实现。这是我提出的示例:
#include <cassert>
#include <iostream>
#include <iomanip>
class ByteReg {
private:
volatile uint8_t ®
public:
explicit ByteReg(volatile uint8_t ®): reg(reg) { }
ByteReg(const ByteReg&) = delete;
ByteReg operator=(const ByteReg&) = delete;
~ByteReg() = default;
operator uint8_t() { return reg; }
bool test(int i) const
{
assert(i >= 0 && i < 8);
return ((reg >> i) & 1) != 0;
}
};
int main()
{
volatile uint8_t hwReg = 0xaa; // 0x10101010
ByteReg reg(hwReg);
unsigned value = reg;
std::cout << "reg: 0x" << std::hex << std::setw(2) << std::setfill('0')
<< value << '\n';
for (int i = 0; i < 8; ++i) {
std::cout << "bit " << i << ": "
<< (reg.test(i) ? "set" : "unset") << '\n';
}
return 0;
}
输出:
reg: 0xaa
bit 0: unset
bit 1: set
bit 2: unset
bit 3: set
bit 4: unset
bit 5: set
bit 6: unset
bit 7: set
不过,一个独立的函数 testBit()
可能用更少的代码也能做到:
#include <cassert>
#include <iostream>
#include <iomanip>
bool testBit(uint8_t reg, int i)
{
assert(i >= 0 && i < 8);
return ((reg >> i) & 1) != 0;
}
int main()
{
volatile uint8_t reg = 0xaa; // 0x10101010
unsigned value = reg;
std::cout << "reg: 0x" << std::hex << std::setw(2) << std::setfill('0')
<< value << '\n';
for (int i = 0; i < 8; ++i) {
std::cout << "bit " << i << ": "
<< (testBit(reg, i) ? "set" : "unset") << '\n';
}
return 0;
}
我想在 C/C++ 中模拟一个状态寄存器,它应该可以作为 std::bitset and as std::uint8_t 访问。因此,我将它们组合为联合,如下所示:
#include <bitset>
#include <iostream>
union byte {
std::uint8_t uint;
std::bitset<8> bitset;
};
int main(int, char*[])
{
byte b = { .uint = 0b10101010 };
std::cout << "Value of bit 5: "
<< (b.bitset.test(5) ? "true" : "false") << std::endl;
std::cout << "Value of bit 4: "
<< (b.bitset.test(4) ? "true" : "false") << std::endl;
std::cout << "Bitset Output: " << b.bitset << std::endl;
std::cout << "Uint Output: " << static_cast<int>(b.uint) << std::endl;
return 0;
}
当使用 GCC x86_64 8.2 编译时,这似乎按预期工作。但是,我想知道我是否可以期望它在所有情况下都能正常工作,或者我是否可以使用 bitset
、bittest
、...
你在这里尝试用 union
做的事情叫做类型双关,是 C++ 中的未定义行为(你可以阅读更多关于它的信息 in this SO answer),所以它不能保证甚至可以正常工作在同一个编译器上。
此外,即使允许,std::bitset<8>
也不能保证与 std::uint8_t
具有相同的表示形式(事实上,它在任何主要编译器上都没有)。
在您的情况下,您可以使用常规 std::bitset<8>
和 to_ulong
方法。
另一种选择是使用带有 bitset 成员的包装器 class,它会提供从 assign/convert 到 uint8_t
的便捷方法。
此外,如果您只想要 std::bitset<8>
中有限的 API,这可能是个好主意(如果您想将 class 的大小保持为 1 个字节)环绕 std::uint8_t
并手动实现这几个方法(如 test
)。
我采纳了 SomeProgrammerDude 的评论
As for your problem, if you want a type that can act as both a native uint8_t and handle bits in a "nice" way, then you have to implement such a class yourself. If you need it to map to a memory-mapped hardware register it should probably wrap a pointer to the register.
并尝试用 C++ 实现。这是我提出的示例:
#include <cassert>
#include <iostream>
#include <iomanip>
class ByteReg {
private:
volatile uint8_t ®
public:
explicit ByteReg(volatile uint8_t ®): reg(reg) { }
ByteReg(const ByteReg&) = delete;
ByteReg operator=(const ByteReg&) = delete;
~ByteReg() = default;
operator uint8_t() { return reg; }
bool test(int i) const
{
assert(i >= 0 && i < 8);
return ((reg >> i) & 1) != 0;
}
};
int main()
{
volatile uint8_t hwReg = 0xaa; // 0x10101010
ByteReg reg(hwReg);
unsigned value = reg;
std::cout << "reg: 0x" << std::hex << std::setw(2) << std::setfill('0')
<< value << '\n';
for (int i = 0; i < 8; ++i) {
std::cout << "bit " << i << ": "
<< (reg.test(i) ? "set" : "unset") << '\n';
}
return 0;
}
输出:
reg: 0xaa
bit 0: unset
bit 1: set
bit 2: unset
bit 3: set
bit 4: unset
bit 5: set
bit 6: unset
bit 7: set
不过,一个独立的函数 testBit()
可能用更少的代码也能做到:
#include <cassert>
#include <iostream>
#include <iomanip>
bool testBit(uint8_t reg, int i)
{
assert(i >= 0 && i < 8);
return ((reg >> i) & 1) != 0;
}
int main()
{
volatile uint8_t reg = 0xaa; // 0x10101010
unsigned value = reg;
std::cout << "reg: 0x" << std::hex << std::setw(2) << std::setfill('0')
<< value << '\n';
for (int i = 0; i < 8; ++i) {
std::cout << "bit " << i << ": "
<< (testBit(reg, i) ? "set" : "unset") << '\n';
}
return 0;
}