1

C で汎用コンテナーが必要な場合、一般的なアプローチの 1 つは を使用することvoid*です。ジェネリック コンテナーが独自の解放関数を持つカスタム構造体を保持している場合、その関数を要求する可能性があります。

struct Foo {...};
Foo *Foo_Allocate(...);
void Foo_Deallocate(const Foo*);


int main(void)
{
    /* Let's assume that when you create the list you have to
       specify the deallocator of the type you want to hold */
    List *list = List_Allocate(Foo_Deallocate);

    /* Here we allocate a new Foo and push it into the list.
       The list now has possession of the pointer. */
    List_PushBack(list, Foo_Allocate());

    /* When we deallocate the list, it will also deallocate all the
       items we inserted, using the deallocator specified at the beginning */
    List_Deallocate(list);
}

しかし、ほとんどの場合、deallocator 関数の型は、void*

typedef void (*List_FnItemDeallocator)(const void*);

問題は、ではなくFoo_Deallocateがかかることです。署名が一致しない場合でも、関数を渡すことは安全ですか? C ではポインター型は必ずしも同じサイズではないため、おそらくそうではありません。const Foo*const void*

それが不可能な場合は、すべてのデアロケーター関数に、const void*関連する型へのポインターではなく を取得させて、汎用コンテナーと互換性を持たせることをお勧めしますか?

4

2 に答える 2

0

あなたが言ったように、別の関数型へのポインターの割り当ては無効です。

をパラメーターとして取り、void*各関数内でいくつかのチェックを実行して、指定されたポインターが期待される型と一致するかどうかを確認する必要があります (構造体の先頭にあるマジック ナンバーのチェックなど)。

于 2012-05-02T17:07:01.523 に答える
0

前述のように、マジック ナンバーまたは「ヘッダー」を使用してデストラクタ関数を指定できます。このヘッダーを使用してかなり遠くまで行くことができ、「よく知られている登録済み」のデアロケーターを選択することもできます (この場合、実際には関数ポインターを格納する必要はなく、おそらく配列への整数インデックスを格納する必要があります)。これが「拡張」デロケーターを含むことを指定する、ヘッダー内の flags セクション。可能性はかなり遠く、とても楽しいです。

したがって、リストの「ヘッダー」は次のようになります

#define LIST_HEAD struct list *next; struct list *prev; short flags;
struct list { LIST_HEAD };
struct list_with_custom_deallocator { LIST_HEAD void (*dealloc)(void*); };

さて、実際にあなたの質問に答えます..共通のヘッダータイプを定義するだけでなく、デアロケータにそのタイプ(私の例では a struct list*)へのポインタを取り、それを特定の関連するタイプにキャストさせないでください。実際の構造体とデアロケーターをヒューリスティックに決定しますflags(Binyamin のヒントに従って)。

于 2012-05-02T17:19:31.743 に答える