假设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一起使用,它们将在程序开始时用于分发簿记所需的一些数据,因此相同的程序将永远是序列化和反序列化数据。
答案 0 :(得分:2)
我看到了几个问题。一个小问题:
*(T*)dest = source;
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:对缓冲区的对齐没有要求。