2017-10-07 50 views
5

假设T是一个不包含指针的POD类型,并且我想序列化T(除了其他一些数据)。我创建了下面的函数来做到这一点:通过直接转换为char数组来序列化POD数据是否安全?

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

我不知道你是如何打破严格别名规则,因为'T *'到'char *'并返回到'T *'应该是一个有效的..我认为这就是'reinterpret_cast'的原因。 – Brandon

回答

2

我看到了一些问题。 A小调之一:

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。

+0

我还没有听说过通过'char *'来定义仅用于读取的别名。你有任何参考? – HolyBlackCat

+0

我的不好。你既可以通过'char *'指针来读写对象,反之亦然(即将'char *'强制转换为'T *'并将其解引用)。 –

1

*(T*)dest = source;是一个严格的锯齿违规。

相反,你应该写:

memcpy(dest, &source, sizeof source); 

您可以复制POD物体周围成功地利用memcpy

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

0

是,可以只要所述缓冲液是炭,无符号的字符或std ::字节的阵列做,C++标准[basic.type]:

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

#define N sizeof(T) 
char buf[N]; 
T obj; //obj initialized to its original value 
std::memcpy(buf, &obj, N);// between these two calls to std::memcpy,obj might be modified 
std::memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type holds its original value 

- 端示例]

诺塔:有上缓冲的取向没有要求。