首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >是否有可移植的替代C++位字段的方法

是否有可移植的替代C++位字段的方法
EN

Stack Overflow用户
提问于 2015-07-30 22:14:43
回答 5查看 6K关注 0票数 22

在很多情况下(尤其是在低级编程中),数据的二进制布局很重要。例如:硬件/驱动程序操作、网络协议等。

在C++中,我可以使用char*和按位操作(掩码和移位)读/写任意二进制结构,但这样做很繁琐且容易出错。显然,我试图限制这些操作的范围,并将它们封装在更高级别的API中,但这仍然是一件痛苦的事情。

C++ bitfields似乎为这个问题提供了一个开发人员友好的解决方案,但不幸的是,它们的存储是implementation specific

NathanOliver提到了std::bitset,它基本上允许你用一个很好的operator[]访问整数的单个位,但缺少多位字段的访问器。

使用元编程和/或宏,可以抽象库中的逐位操作。因为我不想重复发明轮子,所以我正在寻找一个(最好是STL或boost)库来做这件事。

根据记录,我正在寻找一个解析器,但问题及其解决方案应该是通用的。

编辑:short answer:事实证明,bitfield的存储在实践中是可靠的(即使它不是标准规定的),因为系统/网络库使用它们,并且在使用主流编译器编译时产生性能良好的程序。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2017-08-16 12:13:31

在生产代码中,我们必须将MIPS代码移植到x86-64

https://codereview.stackexchange.com/questions/54342/template-for-endianness-free-code-data-always-packed-as-big-endian

对我们来说效果很好。

它基本上是一个没有任何存储的模板,模板参数指定了相关位的位置。

如果需要多个字段,您可以将模板的多个专门化放在一个联合中,并使用一个字节数组来提供存储。

该模板具有用于赋值的重载和用于读取值的unsigned转换运算符。

此外,如果字段大于一个字节,它们将以big-endian字节顺序存储,这在实现跨平台协议时有时很有用。

下面是一个使用示例:

union header
{
    unsigned char arr[2];       // space allocation, 2 bytes (16 bits)

    BitFieldMember<0, 4> m1;     // first 4 bits
    BitFieldMember<4, 5> m2;     // The following 5 bits
    BitFieldMember<9, 6> m3;     // The following 6 bits, total 16 bits
};

int main()
{
    header a;
    memset(a.arr, 0, sizeof(a.arr));
    a.m1 = rand();
    a.m3 = a.m1;
    a.m2 = ~a.m1;
    return 0;
}
票数 5
EN

Stack Overflow用户

发布于 2015-07-30 23:21:27

来自C++14标准(N3797草案),第9.6节class.bit,第1段:

类对象中位字段的

分配是由实现定义的。位字段的对齐是由实现定义的。位字段被打包到一些可寻址的分配单元中。注意:在一些机器上,位域跨越分配单元,而在另一些机器上则不是。位域在一些机器上是从右到左分配的,在另一些机器上是从左到右分配的。-结束注释

尽管notes是非标准化的,但我所知道的每个实现都使用两种布局中的一种:大端或小端位顺序。

请注意:

填充您必须手动指定填充。这意味着你必须知道你的类型的大小(例如,通过使用<cstdint>).

  • You必须使用无符号类型。用于检测位顺序的预处理器宏是implementation-dependent.
  • Usually
  • 位顺序字节序与字节顺序字节序相同。我相信有一个编译器标志可以覆盖它,但是我找不到它。

例如,查看netinet/tcp.h和其他附近的标头。

由OP编辑:例如,tcp.h定义

struct
{
    u_int16_t th_sport;     /* source port */
    u_int16_t th_dport;     /* destination port */
    tcp_seq th_seq;     /* sequence number */
    tcp_seq th_ack;     /* acknowledgement number */
# if __BYTE_ORDER == __LITTLE_ENDIAN
    u_int8_t th_x2:4;       /* (unused) */
    u_int8_t th_off:4;      /* data offset */
# endif
# if __BYTE_ORDER == __BIG_ENDIAN
    u_int8_t th_off:4;      /* data offset */
    u_int8_t th_x2:4;       /* (unused) */
# endif
    // ...
}

由于它可以与主流编译器协同工作,这意味着位集的内存布局在实践中是可靠的。

编辑:

这在一个字节顺序中是可移植的:

struct Foo {
    uint16_t x: 10;
    uint16_t y: 6;
};

但这可能不是因为它跨越了一个16位单元:

struct Foo {
    uint16_t x: 10;
    uint16_t y: 12;
    uint16_t z: 10;
};

这可能不是因为它有隐式填充:

struct Foo {
    uint16_t x: 10;
};
票数 9
EN

Stack Overflow用户

发布于 2015-07-30 23:31:10

使用C++实现具有已知位置的位域非常简单:

template<typename T, int POS, int SIZE>
struct BitField {
    T *data;

    BitField(T *data) : data(data) {}

    operator int() const {
        return ((*data) >> POS) & ((1ULL << SIZE)-1);
    }

    BitField& operator=(int x) {
        T mask( ((1ULL << SIZE)-1) << POS );
        *data = (*data & ~mask) | ((x << POS) & mask);
        return *this;
    }
};

例如,上面的玩具实现允许在unsigned long long变量中定义一个12位字段

unsigned long long var;

BitField<unsigned long long, 7, 12> muxno(&var);

而生成的访问字段值的代码只是

0000000000000020 <_Z6getMuxv>:
  20:   48 8b 05 00 00 00 00    mov    0x0(%rip),%rax  ; Get &var
  27:   48 8b 00                mov    (%rax),%rax     ; Get content
  2a:   48 c1 e8 07             shr    $0x7,%rax       ; >> 7
  2e:   25 ff 0f 00 00          and    $0xfff,%eax     ; keep 12 bits
  33:   c3                      retq   

基本上你必须手写的东西

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31726191

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档