C++ 64位整数句柄类型的简明位操作

C++ 64位整数句柄类型的简明位操作,c++,bit-manipulation,handle,bit-fields,C++,Bit Manipulation,Handle,Bit Fields,我有一个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) str

我有一个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位

  • 只要尊重每个字段的大小,这些字段在内存中的排列顺序就无关紧要

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


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

bool Handle::isMutable() const {
    return bits & MUTABLE;
}

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

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

bool Handle::isMutable() const {
    return bits & MUTABLE;
}

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

我会选择位域解决方案

如果要以二进制形式存储,然后使用不同的编译器或更常见的是在不同的机器体系结构上读取位字段,则位字段仅为“不可移植”。这主要是因为标准没有定义字段顺序

在应用程序中使用位字段是可以的,只要您不需要“二进制可移植性”(将
句柄存储在文件中,并使用不同编译器或不同处理器类型编译的代码在不同系统上读取),它就可以正常工作

显然,您需要进行一些检查,例如,
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位类型是更好的选择]

我会选择位字段解决方案

如果要以二进制形式存储,然后使用不同的编译器或更常见的是在不同的机器体系结构上读取位字段,则位字段仅为“不可移植”。这主要是因为标准没有定义字段顺序

在应用程序中使用位字段是可以的,只要您不需要“二进制可移植性”(将
句柄存储在文件中,并使用不同编译器或不同处理器类型编译的代码在不同系统上读取),它就可以正常工作

显然,您需要进行一些检查,例如,
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位类型是一个更好的选择]

我不会说位字段“有问题”,而且您还没有将可移植性列为一项要求,但它们确实以您可以自己编写得更好的速度生成代码,例如将所有字段清除为零,或同时设置或清除两个字段。除非你有严格的代码空间限制,否则它实际上取决于你想写什么样的代码。根据你的要求,位域似乎是最简单的解决方案。不可移植性来自于不同的编译器可能会对它们进行不同的排序。但是您应该对所有这些类型使用
uint64\u t
。“句柄”类型的要点是它对存储句柄的任何代码都是不透明的。只有创建句柄的代码才会解释其内容。这当然意味着可移植性并不重要。我不会说位字段是“有问题的”,您也没有将可移植性列为一项要求,但它们确实以一定的速度生成代码,您可以自己编写得更好,例如将所有字段清除为零,或者同时设置或清除两个字段。除非你有严格的代码空间限制,否则它实际上取决于你想写什么样的代码。根据你的要求,位域似乎是最简单的解决方案。不可移植性来自于不同的编译器可能会对它们进行不同的排序。但是您应该对所有这些类型使用
uint64\u t
。“句柄”类型的要点是它对存储句柄的任何代码都是不透明的。只有创建句柄的代码才会解释其内容。这当然意味着可移植性并不重要。我觉得这是一个糟糕的建议。在规定的限制条件下,自行编写移位和掩码的代码似乎完全没有好处,即使是最好的情况也是如此。在规定的限制条件下,自行编写移位和掩码的代码似乎完全没有优势,即使是最好的