【问题标题】:Variable-sized bitfields with aliasing具有别名的可变大小位域
【发布时间】:2009-10-01 11:58:36
【问题描述】:

我有一些包含位域的结构,其大小可能会有所不同。示例:

struct BitfieldSmallBase {
    uint8_t a:2;
    uint8_t b:3;
    ....
}

struct BitfieldLargeBase {
    uint8_t a:4;
    uint8_t b:5;
    ....
}

和一个联合来一次访问所有位:

template<typename T>
union Bitfield 
{
    T bits;
    uint8_t all;    // <-------------  Here is the problem

    bool operator & (Bitfield<T> x) const {
        return !!(all & x.all);
    }
    Bitfield<T> operator + (Bitfield<T> x) const {
        Bitfield<T> temp;
        temp.all = all + x.all;   //works, because I can assume no overflow will happen
        return temp;
    }
    ....
}

typedef Bitfield<BitfieldSmallBase> BitfieldSmall;
typedef Bitfield<BitfieldLargeBase> BitfieldLarge;

问题是:对于某些位域基类,uint8_t 是不够的。 BitfieldSmall 确实适合 uint8_t,但 BitfieldLarge 不适合。数据需要尽可能紧密地打包(稍后将由 SSE 指令处理),因此始终使用 uint16_t 是没有问题的。有没有办法用整数类型声明“全部”字段,其大小与位域相同?还是以其他方式访问整个位?

我当然可以放弃使用模板并明确声明每种位域,但我想避免代码重复(有很多运算符和成员函数)。

【问题讨论】:

    标签: c++ bit-fields


    【解决方案1】:

    您也可以将整数类型设为模板参数。

    template<typename T, typename U>
    union Bitfield 
    {
        T bits;
        U all;
    }
    
    typedef Bitfield<BitfieldSmallBase, uint8_t>  BitfieldSmall;
    typedef Bitfield<BitfieldLargeBase, uint16_t> BitfieldLarge;
    

    【讨论】:

    • 有时解决方案就是这么简单:-)
    【解决方案2】:

    我了解到,虽然您正在使用的 var 的位宽是让编译器为您进行屏蔽和移位的一种便捷方式,但您不能做出假设关于结构中成员的顺序和填充。它的编译器依赖并且编译器确实改变了顺序,并且依赖于你项目中的其他代码。

    如果您想将一个字节视为离散字段,您真的必须这样做。

    【讨论】:

    • 可移植性是次要的,因为无论如何我都需要使用 SSE,所以这仅限于 x86_64(可能还有 x86)。对于我使用的编译器(gcc,icc),我可以对结构的布局做出安全的假设,它在 ABI 中定义。
    • 但不保证在两个编译器中相同。或者在这些编译器的下一个版本中。
    • icc 和 gcc 都符合 x86_64 ABI。 ABI 损坏时有发生,但他们通常会非常努力地将影响降至最低。如果你发现 icc 的结构布局与 gcc 不同的情况,你甚至可以提交一个 bug,因为 icc 尽可能地与 gcc 兼容。
    • 好吧,我自己遇到问题的是 GCC 和 x86_64,我想我使用的是 4.4.something。只是我的数据点。
    • 您能详细说明问题出在哪里吗?我知道甚至一些 gcc 开发人员都回避位域,但有时它们对于低级的东西很方便
    【解决方案3】:

    您可以使用模板元编程来定义一个模板函数,该函数从 BitfieldSmallBase、BitfieldLargeBase 等映射到另一种类型 - 默认为 uint8_t,并将 BitfieldLargeBase 的 uint16_t 作为模板特化,然后像这样使用它:

    union Bitfield 
    {
        T bits;
        typename F<T>::holder_type all;
    };
    

    【讨论】:

    • 另一个不错的解决方案。但我更喜欢 John Kugelmans 的解决方案,它更简单。
    • 如果 BitfieldSmall* 类很少,John 有一个更好的解决方案;如果它们中有很多,但我的更好,但有一两个突出并需要,比如 uint16_t 与默认 uint8_t。
    • 有 3 个位域类,其中一个是两字节大小的(到目前为止..)。因为 F::holder_type 的声明及其特化超过 3 行,Johns 解决方案在这种情况下更好,尽管差距很小;-)
    【解决方案4】:

    您可能需要考虑std::bitsetboost::dynamic_bitset 而不是自己动手。无论如何,避开std::vector&lt;bool&gt;

    【讨论】:

    • 感谢您的警告 ;-) 我在这部分程序中使用的数组大小非常固定(与底层数据结构相反),所以无论如何我都避免使用 std::vector。而 boost 不会简化事情,因为我需要通过 SSE(__m128i 等)访问数据来完成实际工作。
    【解决方案5】:

    将你需要的字节数作为模板参数的一部分:

    template <typename T, int S=1>
    struct BitField 
    {
       union
       {
          T bits;
          unsigned char bytes[S];
       };
    };
    
    typedef Bitfield<BitfieldSmallBase, 1>  BitfieldSmall;
    typedef Bitfield<BitfieldLargeBase, 2> BitfieldLarge;
    

    【讨论】:

      【解决方案6】:

      这个怎么样?

      #include <limits.h>
      
      template <class T>
      union BitField
      {
          T bits;
          unsigned all : sizeof(T) * CHAR_BIT;
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-07
        • 2017-07-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多