从union中访问位而不用C ++进行移位

时间:2011-12-05 14:43:30

标签: c++

我正在为我的游戏编写数据包装器。我们的想法是编写可以打包属性,通过网络传输数据以及可能需要数据打包的其他区域的东西。我使用http://cplusplus.com/articles/zb07M4Gy/版本来实现我的目标。我的代码如下:

namespace detail
{
    // Determine whether _Type is signed or unsigned.
    // _Result is then either a signed or unsigned char
    template < typename _Type >
    struct sign
    {
        typedef typename std::_If< std::tr1::is_unsigned< _Type >::value, unsigned char, signed char >::_Type _Result;
    };
}

// Template for packing and unpacking data
// at bit and byte level
template < typename _Type >
struct packer
{
    typedef typename _Type  value_type;

    static const int num_bytes = sizeof(value_type);
    static const int num_bits  = num_bytes * CHAR_BIT;

    union
    {
        value_type packed_value;
        typename detail::sign< value_type >::_Result byte[num_bytes];
    };
};

我的问题是,如何扩展此代码以允许在不进行位移的情况下访问各个位?我不能使用std :: bitset,因为它有一个非平凡的构造函数和一个bool数组会导致为每个条目而不是一个位分配一个字节。

理想情况下,我想避免位设置/检查宏(尽管看起来似乎我的双手可能并列)。有可移植性和endian问题,但除此之外,有没有办法实现我的目标?

我的想法是使用类似的东西:

template< int _NumBits >
struct bit_field
{
    bit_field< _NumBits - 1 > the_other_bits;

    int value : 1;

    int operator[](const int in_index)
    {
        return reinterpret_cast< int* >(this)[index];
    }
};

template<  >
struct bit_field< 0 >
{
    int value : 1;

    int operator[](const int in_index)
    {
        return reinterpret_cast< int* >(this)[index];
    }
};

哪个会以递归方式构建数组。然而,这个(显然)的问题是对齐,对于这个结构,在MSVC10中为4字节,给封装器结构总共132个字节,这在处理4字节整数时显然不理想...

欢迎任何建议(包括“男人和使用位移”......)。

4 个答案:

答案 0 :(得分:3)

您可以使用位域:

struct byte
{
    bool bit0 : 1;
    bool bit1 : 1;
    bool bit2 : 1;
    bool bit3 : 1;
    bool bit4 : 1;
    bool bit5 : 1;
    bool bit6 : 1;
    bool bit7 : 1;
};

然后在你的工会

union something
{
    byte bytes[num_of_bytes];

};

答案 1 :(得分:1)

通过使用位移来保护你的未来几个小时的脑痛。

更具体地说,在进行这种级别的位打包时,您不希望在更高级别中将其抽象出来并相信编译器可以执行您期望的操作。您应明确说明并记录通过网络传输的数据的比特大小,顺序和布局,以及它们存储在您的特定平台上的顺序。然后,您将需要一个特定于平台的部分来处理存储器从一种处理器类型到另一种处理器类型的不同这一事实。您仍然可以编写忽略位级结构的客户端代码,但您的数据打包代码应尽可能明确。

答案 2 :(得分:1)

通过自定义打包和拆包,您需要提前知道打包数据中的数据类型,以便打开包装。这意味着您将在使用数据的部件和部件包装之间进行大量耦合。解包数据。

哦,我添加了一个属性,让我们修改我的打包器/解包器以考虑到这一点。它糟透了,它不会为你节省那么多空间。

因此,如果您真的担心必须发送的数据量,请使用压缩,如lzo或gzip,甚至什么也不做。

这样您就不需要对每种数据类型都有两种表示形式。您可以在不压缩的情况下调试发送/接收代码,然后在不告知程序其余部分的情况下添加压缩/解压缩。

关于这个问题,如果你还打算打包: 首先使用位移。如果在某些时候你发现它很糟糕且容易出错......, 至少你会有一个工作的例子基础来找到更通用的方法。

首先编写通才方法通常意味着过度工程或弄错。从一个更简单和临时的方法开始意味着当你到达目的地时,你最终需要一些新的东西,你有一些解决问题的经验。

答案 3 :(得分:0)

您可以定义一组与要检查的属性的位位置相关的常量:

const UINT8 BITPOS_ATT_ISMALE =  1;
const UINT8 BITPOS_ATT_ISCUST =  2;
const UINT8 BITPOS_ATT_NOCALL =  4; 
const UINT8 BITPOS_ATT_NOSMS  =  8; //and so on for 16, 32, 64, however many you need

然后你不需要转换只需使用按位AND来确定属性是否已设置。

EG:

bool isAttSet(UINT8 attributeToCheck, UINT8 attributeSet)
{
    return ( attributeToCheck &  attributeSet);
}

输入01001101对BITPOS_ATT_ISMALE,BITPOS_ATT_NOCALL和BITPOS_ATT_NOSMS返回true,但不返回BITPOS_ATT_ISCUST。

编辑:为了清楚起见,您将传递“packed_value”,并且您需要根据模板类匹配属性大小。