C++でstruct
を使用している場合、クロスプラットフォーム/コンパイラ互換のファイルに安全に読み書きする方法はありませんか?
正しく理解すれば、すべてのコンパイラがターゲットプラットフォームに基づいて異なる方法で「パディング」するからです。
いいえ。それは不可能です。これは、バイナリレベルでのC++の標準化の欠如が原因です。
ドンボックス 書き込み(彼の本から引用 Essential COM 、章COM As A Better C++)
C++と移植性
C++クラスをDLLとして配布することが決定されると、C++の基本的な弱点の1つ、つまりバイナリレベルでの標準化の欠如。 ISO/ANSI C++ドラフトワーキングペーパーは、どのプログラムをコンパイルし、それらを実行することによるセマンティック効果をコード化しようとしますが、C++のバイナリランタイムモデルの標準化は試みません。この問題が最初に明らかになるのは、クライアントがC++開発環境からFastString DLLのインポートライブラリにリンクしようとしたときです以外 FastString DLLのビルドに使用された環境。
構造体のパディングは、コンパイラごとに異なります。同じコンパイラを使用している場合でも、構造体のパッキングのアライメントは、使用している プラグマパック によって異なる場合があります。
メンバーがexactly同じである2つの構造体を記述する場合だけでなく、onlyの違いは、宣言される順序が異なることです。各構造体のサイズは異なる場合があります(多くの場合、異なります)。
たとえば、これを参照してください、
struct A
{
char c;
char d;
int i;
};
struct B
{
char c;
int i;
char d;
};
int main() {
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
}
gcc-4.3.4
でコンパイルすると、次の出力が得られます。
8
12
つまり、両方の構造体のメンバーが同じであっても、サイズは異なります!
Ideoneのコード: http://ideone.com/HGGVl
一番下の行は、標準はパディングの実行方法については言及していないため、コンパイラーは自由に決定を下すことができ、すべてのコンパイラーが同じ決定をすることを前提としていますcannot 。
自分で構造体を設計する機会があれば、それは可能です。基本的な考え方は、パッドバイトを挿入する必要がないように設計することです。 2つ目のトリックは、エンディアンネスの違いに対処する必要があるということです。
スカラーを使用して構造体を構築する方法を説明しますが、含まれる各構造体に同じ設計を適用する限り、ネストされた構造体を使用できるはずです。
まず、CおよびC++の基本的な事実は、型のアライメントが型のサイズを超えることはできないということです。その場合、malloc(N*sizeof(the_type))
を使用してメモリを割り当てることはできません。
最大のタイプから始めて、構造体をレイアウトします。
struct
{
uint64_t alpha;
uint32_t beta;
uint32_t gamma;
uint8_t delta;
次に、構造体を手動でパディングして、最終的に最大のタイプに一致するようにします。
uint8_t pad8[3]; // Match uint32_t
uint32_t pad32; // Even number of uint32_t
}
次のステップでは、構造体をリトルエンディアン形式で保存するかビッグエンディアン形式で保存するかを決定します。ストレージシステムがホストシステムのエンディアンと一致しない場合、構造体の書き込み前または読み取り後に、すべての要素をin sitで「スワップ」するのが最善の方法です。
いいえ、安全な方法はありません。パディングに加えて、さまざまなバイト順序、およびさまざまなサイズの組み込み型を処理する必要があります。
ファイル形式を定義し、構造体とその形式を変換する必要があります。シリアル化ライブラリ(例:boost :: serialization、またはgoogleのprotocolbuffers)はこれに役立ちます。
長い話、短い。プラットフォームに依存せず、標準に準拠したパディング処理方法はありません。
パディングは標準では「整列」と呼ばれ、3.9/5で議論が始まります。
オブジェクトタイプには位置合わせの要件があります(3.9.1、3.9.2)。完全なオブジェクト型のアライメントは、バイト数を表す実装定義の整数値です。オブジェクトは、そのオブジェクトタイプのアライメント要件を満たすアドレスに割り当てられます。
しかし、それはそこから続き、標準の多くの暗いコーナーに曲がりくねります。アライメントは「実装定義」です。つまり、異なるコンパイラ間で、または同じコンパイラの下のアドレスモデル(つまり32ビット/ 64ビット)間でも異なる場合があります。
本当に厳しいパフォーマンス要件がない限り、char文字列などの異なる形式でデータをディスクに保存することを検討してください。多くの高性能プロトコルは、自然な形式が他の形式である可能性がある場合、文字列を使用してすべてを送信します。たとえば、私が最近取り組んだ低遅延交換フィードは、「20110321」という形式の文字列として日付を送信し、同様に「141055.200」という時刻が送信されます。このエクスチェンジフィードは1日あたり1秒間に500万のメッセージを送信しますが、エンディアンなどの問題を回避できるため、すべてに文字列を使用します。
boost::serialization
のようなものを使用できます。