4

私は、顧客に静的 c ライブラリとして提供される大規模なプロジェクトと、公開された API のプロトタイプを含む 1 つのヘッダー ファイルに取り組んでいます。このプロジェクトには、ヘッダーに異なるデータ構造を公開する必要がある、似ているが独自のビルドが 2 つあります。私は、ビルドに応じて単一の API 関数が異なるデータ構造で動作できるようにするための最適な設計を考え出そうとしています。これは今のところ私の考えですが、これは悪い設計ではないかと心配しています。

私の機能はこのように実装されます

void foo(void *generic_data_struct)
{
#ifdef BUILD1
   build1_t *data_struct = generic_data_struct;
#else
   build2_t *data_struct = generic_data_struct;
#endif
...
}

また、公開された API ヘッダーは、顧客が注文したビルドに応じて、次のいずれかになります。

void foo(build1_t *data_struct);

また

void foo(build2_t *data_struct);

これは合理的な設計パターンですか、それとも眉をひそめていますか? ありがとう!

4

4 に答える 4

5

ほとんどの場合、この種のものにはポインターstruct foo;ではなく不透明型 ( )を使用することをお勧めします。void

バリアント ビルドがある場合は常に、#ifdef の数を最小限に抑えることが非常に望ましいです。eq- の提案を使用することをお勧めしますが、typedef を使用して関数プロトタイプのバリエーションを除外します。

/* header file */
#ifdef BUILD1
typedef build1_t generic_t;
#else
typedef build2_t generic_t;
#endif

void foo(generic_t *);
void bar(generic_t *);
/* etc */

に基づいて実際の動作が変更されるたびに、 、 などの内部 に#ifdefs が必要ですが、すべての関数の宣言と定義では必要ありません。foobarBUILD1

于 2012-08-28T18:04:20.020 に答える
3

なぜこれが好きではないのですか:

#ifdef BUILD1
foo(build1_t *data_struct)
#else
foo(build2_t *data_struct)
#endif
{
  /* ... */
}

AFAIK、標準では、異なるポインター型がビット単位で互換性がある必要はないため、元のアイデアはすべての可能な実装に移植できない場合があります。プロトタイプも使用していれば、そうなるでしょうvoid*(ただし、もちろん、その過程で型の安全性がいくらか犠牲になります)。

于 2012-08-28T18:01:01.903 に答える
1
struct generic_struct {
    union {
        struct specific_struct_1 struct_1;
        struct specific_struct_2 struct_2;
    };
};

これにより、重複するスペースに両方の構造定義を保持する単一の型が得られます。

于 2012-08-28T18:04:09.997 に答える
1

コンパイル時のディレクティブを使用して機能を分離しないことをお勧めします。実行時に選択します。if() ステートメントを使用し、モードを示すいくつかの状態を持っています。

于 2012-08-28T18:08:03.573 に答える