2017-10-07 6 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; 
} 

はエヴァーfalseを返しますか? (TはPODであり、誰も==演算子をオーバーロードしていないと仮定します)。

私はこれらのシリアル化メソッドをMPIと共に使用しています。これらのメソッドは、プログラムの開始時に簿記に必要なデータの一部を配布するために使用されるため、同じプログラムは常にシリアル化およびデシリアライズされますデータ。

+0

「T *」から「char *」へ、そして「T *」から「T *」に戻って、厳密なエイリアスの規則を破っているかどうかはわかりませんが、私は 'reinterpret_cast' – Brandon

答えて

2

私にはいくつかの問題があります。マイナー1:

IIRCが、これは他のポインタを別名設定できる(なぜならエイリアシング規則違反の、char * UBであるが、これはあなたがchar *ポインタを経由して、いくつかのオブジェクトにアクセスできることを意味はなく、その逆、あなたの例のように)。言い換えれば

、コードは以下となります。 を... エヴァーfalseを返しますか?

恐らくはそうではありませんでしたが、あなたは1つのオブジェクトだけでなく、シリアル化すると述べました。

ので、大きな問題は、アライメントです:

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では、Tにはoperator==が定義されていないため、コンパイルに失敗します。(なし暗黙的に生成された比較演算子があります)

0

はい、あなたがいる限り、バッファは、charの配列、unsigned char型またはSTD ::バイト、C++標準[basic.type]であるようにそれを行うことができます。

トリビュアコピー可能なタイプT の任意のオブジェクト(基本クラスのサブオブジェクト以外)に対して、オブジェクトがタイプTの有効な値を保持しているかどうかにかかわらず、オブジェクトを構成する基礎となるバイト(4.4) char、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 

- エンド例]

注意:バッファの位置合わせに必要はありません。

関連する問題