64位整型句柄类型的简洁位操作

Concise bit-manipulation for 64bit integer handle type

本文关键字:简洁 位操作 类型 整型 句柄 64位      更新时间:2023-10-16

我有一个64位整数用作句柄。64位必须分割成以下字段,以便单独访问:

size           : 30 bits
offset         : 30 bits
invalid flag   : 1 bit
immutable flag : 1 bit
type flag      : 1 bit
mapped flag    : 1 bit

我能想到的两种方法是:

1)传统的位操作(& | << >>)等。但我觉得这有点神秘。

2)使用位域结构体:

#pragma pack(push, 1)
struct Handle {
    uint32_t size      : 30;
    uint32_t offset    : 30;
    uint8_t  invalid   : 1;
    uint8_t  immutable : 1;
    uint8_t  type      : 1;
    uint8_t  mapped    : 1;
};
#pragma pack(pop)

那么访问一个字段就变得非常清楚了:

handle.invalid = 1;

但是我知道位域很有问题,而且不可移植。

我正在寻找实现这个位操作的方法,以最大限度地提高代码的清晰度和可读性。我应该采取哪种方法?

指出:

  • 句柄大小不能超过64位;

  • 只要尊重每个字段的大小,这些字段在内存中放置的顺序是无关的;

  • 句柄没有保存/加载到文件中,所以我不必担心尾序

我会选择位域解决方案。

位域只有在您希望以二进制形式存储,然后使用不同的编译器或更常见的是,在不同的机器架构上读取该位域时才"不可移植"。这主要是因为标准没有定义字段顺序。

在您的应用程序中使用位域是可以的,只要您不需要"二进制可移植性"(将您的Handle存储在一个文件中,并在不同的系统上使用由不同的编译器或不同的处理器类型编译的代码来读取它),它就会工作得很好。

显然,你需要做一些检查,例如sizeof(Handle) == 8应该在某个地方完成,以确保你得到正确的大小,编译器还没有决定把你的两个30位值放在单独的32位字中。为了提高在多个体系结构上成功的机会,我可能会将类型定义为:

struct Handle {
    uint64_t size      : 30;
    uint64_t offset    : 30;
    uint64_t invalid   : 1;
    uint64_t immutable : 1;
    uint64_t type      : 1;
    uint64_t mapped    : 1;
};

有一些规则,编译器不应该"拆分元素",如果你定义的东西为uint32_t,只有两个比特留在字段,整个30位移动到下一个32位元素。[它可能在大多数编译器中工作,但以防万一,使用相同的64位类型是一个更好的选择]

我推荐位操作。当然,您应该将所有这些操作隐藏在类中。提供成员函数来执行set/get操作。在类中明智地使用常量将使大多数操作相当透明。例如:

bool Handle::isMutable() const {
    return bits & MUTABLE;
}
void Handle::setMutable(bool f) {
    if (f) 
        bits |= MUTABLE;
    else 
        bits &= ~MUTABLE;
}