0

いくつかのフィールドを持つレコードを格納するCライブラリがあります。スキーマは、レコード内の各フィールドのタイプを含め、テキストファイルから読み込まれます。

質問の目的で単純化するために、私が持っていると想像してください

typedef enum my_type_enum
{
    INT32, //32-bit integer
    MYSTRUCT, //some struct I have, details irrelevant
    ...
} my_type_enum;

typedef struct my_var
{
    my_type_enum typetag;
    unsigned char* data;
} my_var;

my_var myrecord[numfields];

スキーマファイルは、myrecordの各フィールドがint32_tまたはmystructのどちらを保持する必要があるかを示します。私のライブラリはスキーマファイルを読み取り、myrecordのmy_varごとにタグを設定し、データに適切な量のスペースを割り当てます。

my_varは不透明で、クライアントプログラムは基本的に単純なデータに使用します

void set(my_var* record, size_t field, void * src)
{
    memcpy(record[field].data, src, datatypes[record[field].typetag].size);
}

int32_t x = 5; 
set(myrecord, 0, &x);

レコードに値を格納し、同様のget()を使用して物事を取り出します。

タグ付きのmy_var型を使用すると、データがmy_var内にあると型チェックが可能になりますが、スキーマにレコードが3つのINT32を保持していると示されている場合は、データをset()しようとしているときに、srcがmystructではなくint32_tを指していることを確認する必要はありません。そのmy_varに。

明らかに、int32_t*またはmystruct*がvoid*に変換される前に、set()をラップする何かでチェックを実行する必要があります。typeof()のトリックを使用したコンパイル時のチェックを見てきました。私が望むことはおそらく不可能だと思いますが、あなたはすべてのトリックを知っていることは決してありません...

クライアントプログラムのコンパイル時にスキーマを読み取り、mystructを保持するようにタグ付けされたmy_varにint32_tをコピーしようとするとコンパイラエラーが発生するset_CHECKED()ラッパーマクロを生成する機能を提供するよりも良い方法はありますか?GCC拡張機能は問題ありません。

4

1 に答える 1

0

実際には、'set' と 'get' がチェックを入れる正しい場所のようです。間違った型のフィールドへのアクセスが致命的なエラーである場合、修正は簡単です。

void set(my_var* record, size_t field, void * src)
{
    if (record[field].typetag != (my_var)src->typetag) {
        fprintf(stderr, "Type mismatch!\n");
        exit(1);
    }
    memcpy(record[field].data, src, datatypes[record[field].typetag].size);
}

型の不一致が致命的なエラーでない場合は、設定を行い、戻りコードとエラー コードを取得して、呼び出しサイトで正しく処理する必要があります。

C では、コードがコンパイルされるまでに型情報が失われることに注意してください。「typetag」フィールドは、フィールドのタイプを知る必要がある唯一の方法です。

もちろん、ライブラリを再コンパイルせずにスキーマを変更できる必要がない場合は、ライブラリから C コードを生成してみてください。これにより、コンパイル時にコンパイラの型チェックを使用できます。

(また、大したことではありませんが、my_var.data は char* ではなく void* である必要があります。これはコードの正確性を変更しませんが、void* はリーダー (およびデバッガー) に型が不明であることを伝えます。)

于 2012-06-21T19:48:31.597 に答える