便携式数据对齐解决方案(序列化)

时间:2014-12-08 15:41:13

标签: c++ cross-platform portability

假设我正在尝试从文件中读取数据。数据存储为二进制数据,可以使用编译器打包扩展轻松读取(为清晰起见,使用C99表示法):

#pragma pack(push, 1) /* visual studio */
struct S
{
  int16_t v1;
  uint32_t v2; /* gcc would use: __attribute__((packed)) */
  int16_t v3;
};
#pragma pack(pop)
void read( std::istream & is )
{
  S s;
  assert( sizeof(s) == 8 ); // packed !
  is.read( (char*)&s, sizeof(s) );
  std::cout << s.v1 << " " << s.v2 << " " << s.v3 << std::endl;
}

编写相同的代码但考虑到可移植性变得更加混乱:

struct S2
{
  unsigned char buf1[2];
  unsigned char buf2[4];
  unsigned char buf3[2];
};
static inline uint16_t makenum(const unsigned char (&x)[2])
{
  return x[0] | (x[1] << 8);
}
static inline uint32_t makenum(const unsigned char (&x)[4])
{
  return
      ((uint32_t)x[0] <<  0)
    | ((uint32_t)x[1] <<  8)
    | ((uint32_t)x[2] << 16)
    | ((uint32_t)x[3] << 24);
}
void read( std::istream & is )
{
  S2 s2;
  assert( sizeof(s2) == 8 ); // garanteed !
  is.read( (char*)&s2, sizeof(s2) );
  std::cout << makenum(s2.buf1) << " " << makenum(s2.buf2) << " " << makenum(s.buf3) << std::endl;
}

还有什么(更智能)吗?我想位移和按位包含OR不应该影响执行,但我找不到union的通用解决方案来避免计算。例如:伪解(非工作):

struct S3
{
  union { char buf1[2]; int16_t v1; } uv1;
  union { char buf2[4]; uint32_t v2; } uv2;
  union { char buf3[2]; int16_t v3; } uv3;
};

2 个答案:

答案 0 :(得分:1)

我强烈建议不要使用struct使用协议映射字段1:1。

其中一个原因是允许编译器在字段之间添加填充。

另一个原因是您希望结构对您的处理器有效。希望您更频繁地操作结构中的数据,以便用它执行I / O.

例如,给定一个32位处理器,32位数字效率非常高,但16位处理效率不高。该协议需要16位整数。那么,您是否将结构中的字段映射为16位或32位?

答案:在结构中使用32位字段并编写方法以转换为协议或从协议转换。例如,要从存储器加载16位变量,32位处理器可能必须执行取指和移位,具体取决于16位位于32位寄存器中的位置。如果结构字段是32位,则不需要移位;因此效率更高。

此外,编写协议转换功能可以让您在不改变结构的情况下处理Big-Endian和Little-Endian问题。

答案 1 :(得分:0)

以下是我提出的解决方案:

#include <cstring>
#include <stdint.h>

template <typename T>
struct Fast;
template <>
struct Fast<uint16_t> {
  typedef uint_fast16_t Type;
};
template <>
struct Fast<int16_t> {
  typedef int_fast16_t Type;
};
template <>
struct Fast<uint32_t> {
  typedef uint_fast32_t Type;
};
template <>
struct Fast<int32_t> {
  typedef int_fast32_t Type;
};
template <>
struct Fast<uint64_t> {
  typedef uint_fast64_t Type;
};
template <>
struct Fast<int64_t> {
  typedef int_fast64_t Type;
};

template <typename T>
struct Helper {
  typedef typename Fast<T>::Type RetType;
  typedef char (VecType)[sizeof(T)];
  typedef union { VecType vec; T val; } UType;
};

template <typename T>
struct MakeNum {
  typedef typename Helper<T>::RetType RetType;
  typedef typename Helper<T>::UType UType;

  static RetType Get(const char (&x)[sizeof(T)]) {
    UType u;
    memcpy( u.vec, x, sizeof(T) );
    return u.val;
  }
};


#define AddField( type, name ) \
  char name ## _[sizeof(type)]; \
  typename MakeNum<type>::RetType \
    name () const { return MakeNum<type>::Get(name ## _); }

struct S
{
  AddField( uint16_t, name1 );
  AddField(  int32_t, name2 );
  AddField( uint16_t, name3 );
};

int main()
{
  S s = { 0, 1, 0, 1, 0, 0, 0, 1 };
  return s.name1() + s.name2() + s.name3();
}

使用gcc 4.9.1:

生成与此(非可移植)完全相同的代码
#include <stdint.h>

struct S2
{
  uint16_t v1;
   int32_t v2 __attribute__((packed));
  uint16_t v3;
};

int main()
{
  S2 u = { 256, 256, 256 };
  return u.v1 + u.v2 + u.v3;
}