通过直接转换为char数组来序列化POD数据是否安全?

时间:2017-10-07 21:26:11

标签: c++ serialization mpi

假设T是一个不包含指针的POD类型,我想序列化T(除了一些其他数据)。我已经创建了以下功能来执行此操作:

template<class T> void serialize(const T& source, char*& dest)
{
  *(T*)dest = source;
  dest += sizeof(T);
}
template<class T> void deserialize(T& dest, char*& source)
{
  dest = *(T*)source;
  source += sizeof(T);
}

这是否会导致任何问题,或者是否有任何编译器无法正常工作?换句话说,代码是:

template<class T> bool check_sanity(const T& obj)
{
  std::unique_ptr<char[]> buffer { new int[sizeof(T)] };
  serialize(obj, buffer);
  T new_obj;
  deserialize(new_obj, buffer); 
  return new_obj == obj;
}

永远回归虚假? (假设T是POD,没有人重载==运算符)。

我正在编写这些序列化方法与MPI一起使用,它们将在程序开始时用于分发簿记所需的一些数据,因此相同的程序将永远是序列化和反序列化数据。

3 个答案:

答案 0 :(得分:2)

我看到了几个问题。一个小问题:

*(T*)dest = source;
IIRC,这是UB,因为别名规则违规(char *可以为任何其他指针设置别名,但这意味着您可以通过char *指针访问某个对象,但反之亦然,如你的例子)。

  

换句话说,代码是:    ...    曾经回归虚假?

可能不是,但你提到的不只是序列化一个对象。

因此,主要问题是路线

std::unique_ptr<char[]> buffer { new char[sizeof(int) + 1] };
char x = 0;
int y = 0;
serialize(x, buffer);
serialize(y, buffer); // may crash or write into wrong location

错误的行是相同的(但deserialize也受到影响):

*(T*)dest = source; // source is int, dest is not aligned

编译器将假定dest已正确对齐并使用CPU指令进行对齐存储(在ARM体系结构上,这将导致实际问题)。

解决方案是使用memcpy:

memcpy(dest, &source, sizeof(T));

无需担心性能问题。现代编译器能够很好地优化已知大小的对象的memcpy。

答案 1 :(得分:1)

*(T*)dest = source;是严格的别名违规行为。

相反,你应该写:

memcpy(dest, &source, sizeof source);

您可以使用memcpy成功复制POD对象。

check_sanity函数中,它将无法编译,因为operator==未定义T。 (没有隐式生成的比较运算符)

答案 2 :(得分:0)

是的,你可以这样做,只要缓冲区是char,unsigned char或std :: byte,C ++标准[basic.type]的数组:

  

对于普通可复制类型T的任何对象(基类子对象除外)   ,无论对象是否包含类型T的有效值,组成对象的基础字节(4.4)都可以复制到char,unsigned char,orstd :: byte(21.2.1)的数组中。如果将该数组的内容复制回对象,则该对象应随后保持其原始值。 [实施例

[x]
  

- 结束示例]

Nota:对缓冲区的对齐没有要求。