2012-06-27 9 views
5

基本的には、構造体の正確な定義に関連付けられた、ある種のコンパイル時生成バージョンが必要です。構造体の定義が変更された場合(フィールドの追加、移動、、おそらくの名前が変更された場合)、そのバージョンも変更します。構造体定義のバージョンIDを生成しますか?

このようなバージョン定数は、以前にシリアライズされた構造体を読み込み、それがまだ互換性があることを確認するときに便利です。代わりに、手作業で指定された定数を手動で追跡することができます。これは、インクリメントを忘れると(混乱を招くデシリアライズによって)ガベージが生成される場合に混乱を招く可能性があります。また、正確にインクリメントするときリリースの)。

これは外部ツールを使用して構造体定義にハッシュを生成することで実現できますが、Cコンパイラ(および/またはそのプリプロセッサ)で可能かどうかは疑問です。

これは実際にはイントロスペクションの形式なので、ANSI Cではこれがまったく可能でない可能性がありますが、gccとclangで動作するソリューションには満足しています。

答えて

2

Windows APIは、構造体の最初のメンバーの1つとしてsizeメンバを持っています(例としてWNDCLASSEX参照):

struct Foo 
{ 
    size_t size; 
    char *bar; 
    char *baz; 
    /* Other fields */ 
}; 

そして、あなたはsizeofを使用してサイズを設定して呼び出す前に:

struct Foo f; 

f.size = sizeof(struct Foo); 
f.bar = strdup("hi"); 
f.baz = strdup("there"); 

somefunc(&f); 

その後somefuncは、それが扱った構造体のバージョンsizeメンバー、に基づいて、知っているだろう。 sizeofは実行時ではなくコンパイル時に評価されるため、後方ABI互換性が可能になります。

+0

これに関連するすべてのMS? – ouah

+0

例を追加 –

+0

ありがとうございました。私はsizeofを使用することを考えましたが、移動されたフィールドや以前に見られたサイズになるその他の操作を防ぐことはできません。 Windows APIの場合は、フィールドの追加だけを変更することを制限するのは良い考えですが、現在はそれほど遠くに行きたくはありません。 –

2

自動的に行うことは何もありませんが、あなたはsizeofoffsetofを使用して、それらを組み合わせた順序が重要な方法で組み合わせることができます。ここでは一例です:あなたが見ることができるように

#include <stdio.h> 
#include <stddef.h> 

#define COMBINE2(a,b) ((a)*31+(b)*11) 
#define COMBINE3(a,b,c) COMBINE2(COMBINE2(a,b),c) 
#define COMBINE4(a,b,c,d) COMBINE2(COMBINE3(a,b,c),d) 

typedef struct A { 
    int a1; 
    char a2; 
    float a3; 
} A; 

typedef struct B { 
    int b1; 
    char b2; 
    double b3; 
} B; 

typedef struct C { 
    char c2; 
    int c1; 
    float c3; 
} C; 

typedef struct D { 
    int d1; 
    char d2; 
    float d3; 
    int forgotten[2]; 
} D; 

int main(void) { 
    size_t aSign = COMBINE4(sizeof(A), offsetof(A,a1), offsetof(A,a2), offsetof(A,a3)); 
    size_t bSign = COMBINE4(sizeof(B), offsetof(B,b1), offsetof(B,b2), offsetof(B,b3)); 
    size_t cSign = COMBINE4(sizeof(C), offsetof(C,c1), offsetof(C,c2), offsetof(C,c3)); 
    size_t dSign = COMBINE4(sizeof(D), offsetof(D,d1), offsetof(D,d2), offsetof(D,d3)); 
    printf("%ld %ld %ld %ld", aSign, bSign, cSign, dSign); 
    return 0; 
} 

このコードprints

358944 478108 399864 597272 

、このコードは異なる長さと変化の分野の再順序付けに反応する各構造のための実行時定数を生成しますフィールドの型。また、計算の基礎となるフィールドのリストを更新することを忘れても、フィールドを追加することに反応します。これにより、何らかの安全ネットが生成されるはずです。

+0

ああ、私は 'offsetof'を忘れていました。私はこれがANSI Cで可能な最も近いものだと思いますか? –

+0

@Julienはい、少なくともANSI C内では、これらの2つを組み合わせるよりもはるかにうまくいくとは思えません。あなたはもっと良いCOMBINEを作ることができます。私のものは素早く汚い仕事のように見えますが、さまざまな構造をハッシングするためのプロダクションコード内のものです。 – dasblinkenlight

+0

@Julienもし関数がコンパイル時に計算されなければならないという制約がないなら、構造体フィールドの名前を表す文字列のハッシュを計算することで名前変更を防ぐことができます( '#'プリプロセッサ文字列演算子そのために)。 – dasblinkenlight

関連する問題