以 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 编译时,这似乎按预期工作。但是,我想知道我是否可以期望它在所有情况下都能正常工作,或者我是否可以使用 bitsetbittest、...

等辅助函数更好

你在这里尝试用 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 &reg;
  public:
    explicit ByteReg(volatile uint8_t &reg): 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

Live Demo on coliru

不过,一个独立的函数 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; 
}

Live Demo on coliru