使用固定大小类型时强制位字段符号 (pre-C++14)

Forcing sign of a bit field (pre-C++14) when using fixed size types

基本问题请跳至粗体部分,其余只是背景。

由于我不想深入的原因,我正在编写一个代码生成器,它在(非常)pre-C++14 环境中生成 C++ 结构。生成器必须创建 bit-fields;它还需要尽可能严格地控制生成字段的行为,以尽可能 table 的方式。我需要控制底层分配单元的大小,以及如何处理带符号的值。我不会解释为什么我会做这样一个傻瓜差事,这显然与实现定义的行为相冲突,但涉及到薪水,并且所有正确的方法来做需要做的事情都被人们拒绝了谁安排薪水。

所以我一直在生成类似这样的东西:

int32_t x : 11;

因为我需要让编译器相信这个字段(以及具有相同基础类型的其他相邻字段)存在于 32 位字中。为基础类型生成 int 不是一个选项,因为 int 没有固定的大小,并且当有人发布 int 是 64 位宽的编译器的那一天,事情会变得非常错误,或者我们最终回到一个是 16。

在 pre-C++14 中,int x: 11 可能是也可能不是无符号字段,您可以在前面加上明确的 signedunsigned 以获得您需要的内容.我担心 int32_t 和朋友们会有同样的歧义(为什么不呢?)但编译器正在堵住 signed int32_t

C++ 标准是否有任何关于 intxx_t 类型是否将其符号强加给位字段的说法? 如果没有,是否有任何保证

typedef signed int I32;
...
I32 x : 11;
...
assert(sizeof(I32)==4); //when this breaks, you won't have fun

将带符号的指示符带入位域?

请注意,任何以 "just generate a function to..." 开头的建议都是由 table 决定的。这些生成的 headers 将被插入到执行类似 s->x = 17; 之类的代码中。我已经很好地向我解释说,我绝不能再一次建议将其全部更改为 s->set_x(17)。即使我可以简单地生成一个 set_x 函数来准确、安全地做我需要的事情,而根本不需要任何实现定义的行为。此外,我非常了解位字段的变幻莫测,从左到右、从右到左、从里到外以及编译器处理它们的任何其他方式,以及为什么这是傻瓜差事的其他几个原因。而且我不能只是 "try stuff" 因为这需要在我没有的编译器上工作,这就是为什么我在标准保证之后争先恐后。

注意:我无法实施任何不允许现有代码简单地将指向字节缓冲区的指针转换为指向生成的结构的指针,然后使用它们的指针获取要读取的字段的解决方案和写。现有代码都是关于 s->x 的,并且必须在不做任何更改的情况下工作。这排除了在生成的代码中涉及构造函数的任何解决方案。

it also needs the tightest possible control over the behaviour of the generated fields, in as portable a fashion as possible. I need to control both the size of the underlying allocation unit, and how signed values are handled.

这两个目标是不相容的。位域本身就存在可移植性问题。

如果标准定义了您想要的行为,那么 "vagaries of bit fields" 就不会存在,人们也不会费心推荐使用位掩码和移位来实现可移植性。

您可以做的是提供一个 class,它公开与带有位域的 struct 相同的接口,但实际上并不在内部使用位域。然后,您可以使其构造函数和析构函数通过掩码和移位可移植地读取或写入这些字段。例如,类似于:

class BitfieldProxy
{
public:
    BitfieldProxy(uint32_t& u)
    : x((u >> 4) & 0x7FF),
      y(u & 0xF),
      mDest(u)
    {
    }

    ~BitfieldProxy()
    {
        assert((x & 0x7FF) == x);
        assert((y & 0xF) == y);
        dest = (x << 4) | y;
    }

    BitfieldProxy(const BitfieldProxy&) = delete;
    BitfieldProxy& operator=(const BitfieldProxy&) = delete;

    // Only the last 11 bits are valid.
    unsigned int x;

    // Only the last 4 bits are valid.
    unsigned int y;

private:
    uint32_t& mDest;
};

Does the C++ standard have any words on whether the intxx_t types impose their signedness on bit fields?

没有


<cstdint>[cstdint.syn] (link to modern standard; the relevant parts of the synopsis looks the same in the C++11 standard) 的固定宽度整数的标准概要简单地指定,描述性地(不是通过 signed/unsigned关键字),它们应该是"signed integer type""unsigned integer type".

例如for gcc, <cstdint> expose the fixed width integers of <stdint.h>, which in turn are typedefs to predefined pre-processor macros (e.g. __INT32_TYPE__ for int32_t),后者是特定于平台的。

该标准并未强制要求在此提要中使用 signedunsigned 关键字,因此固定宽度整数类型的位字段将在 C++11 中受到与声明普通整数位字段时存在的关于符号的相同实现定义行为。回想一下 [class.bit]/3 在 C++14 之前的相关部分是(在由于 CWG 739 采取行动之前):

It is implementation-defined whether a plain (neither explicitly signed nor unsigned) char, short, int, long, or long long bit-field is signed or unsigned. ...

确实有以下跟帖

显示了一个示例,例如__INT32_TYPE__ 在回答者的特定平台上定义时没有明确存在 signed 关键字:

$ gcc -dM -E  - < /dev/null | grep __INT
...
#define __INT32_TYPE__ int