2012-05-02 3 views
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); 
} 

しかし、最も可能性の高いデアロケータ関数の型はvoid*

を取り、何かになります:一般的なコンテナは、独自の割り当て解除機能を持ついくつかのカスタム構造体を保持している場合、おそらく、その関数を求めるために起こっています
typedef void (*List_FnItemDeallocator)(const void*); 

Foo_Deallocateconst Foo*で、const void*ではありません。署名が一致しなくても関数を渡すのはまだ安全ですか?おそらくそうではないでしょう。

もしそれができないのであれば、すべてのデアロケータ関数は関連する型へのポインタの代わりにconst void*を取ることをお勧めします。それらは汎用コンテナと互換性がありますか?

+0

このモデルは、不満足なアシンメトリを提供します。クライアントは 'Foo'の割り当てを担当しますが、コンテナはそれらの割り当てを解除します。 –

+0

@OliCharlesworthこれは単純な例です。 'List_Allocate'がデアロケータだけでなくコピー/クローン機能についても尋ねるケースを想像してみてください。また、要素をコピーし、その引数を保持しない 'List_CopyBack'のような関数を提供するかもしれません。誰がこれらのオブジェクトを解放するのは誰の責任ですか? –

答えて

0

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

void*をパラメータとし、各関数の内部でいくつかのチェックを実行して、指定されたポインタが予想される型と一致するかどうかを確認する必要があります。

+0

[2](http://stackoverflow.com/questions/10418354#comment13443285_10418619)[コメント](http://stackoverflow.com/questions/10418354#comment13443335_10418619)私はmnunbergの答えを残しました。 –

+0

@PaulManta:コンパイル時の型チェック、実行時の型チェック、または型チェックを選択することができます。コンパイル時のチェックは、すべてをマクロで書き換えない限り、断念しているので、実行時チェックを行うかどうかを選択するだけです。 –

0

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

だからあなたのリストに「ヘッダ」は、実際に...理由だけで、共通のヘッダタイプを定義し、あなたのデアロケータがそれへのポインタを取るていない、あなたの質問に答える、今、この

#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*); }; 

のようになります。 (私の例ではstruct list*)、それから特定の関連する型にキャストするか、より良い方法で、おそらくヒューリスティックにflags(Binyaminによって暗示されている)から実際の構造とデアロッケータを決定します。

+0

これは、要求がどれほど単純かを考えるとかなり複雑に思えます。*デアロケータを指定できる*。また、エラーが発生しやすいと思われます。すべてのデアロケータで 'void * 'を使うのを避けたいのであれば、これらのデアロッケータの周りにラッパーを作成し、ラッパーを汎用のオブジェクトと互換性を持たせるようにしたいと思います。 –

+0

Cは私の言語ではないので、間違っているかもしれませんが、プログラマがよく知っている*の行に沿ってその哲学があるようです。プログラマは常に注意しています* 。フラグを使用して型を決定し、安全性を少しだけ得ることは、直感的ではなく、維持しやすい、あるいは言語の哲学の特徴でさえもオーバーヘッドを追加するように思えます。 –

+0

_programmerは、自分のフラグを指定してbest_pilosophyが適用されていることを認識します。より高いレベルの言語は、実際には、これらの上位オブジェクトのヘッダーとマジックナンバーの形式を実装しています(したがって、それらはすべて「変数」ですが、それらが定義するフラグや異なる割り振り/割り当て/安全性についてだけでなく、あなたが扱っているオブジェクトのタイプを知ることについても、これは「オブジェクト」または「ジェネリック」システムの中核です。よく知られているフォーマットで共通するものを提供し、その後に差別化仕様 –

関連する問題