1

私は古典的な C を使用してこのクラスの割り当てを行っていますが、可変引数のカウントと型を取るコールバック関数に関するこの問題に悩まされています。

基本的に、私はハッシュ ツリー (各ノードがハッシュ ツリーであるツリー) に取り組んでおり、さまざまな目的で複数回使用される特定のトラバーサル戦略があるため、それを として実装しましht_walk(HashTree tree, (*callback)(Element e))た。コールバックとして呼び出される関数は、必要に応じて要素を処理します。

問題は、私の問題のほとんどの状況で、コールバック関数が異なる引数を取る必要があることです。「可変引数」関数 (stdarg、printf-way を使用) を使用して可変引数リストを持つ関数を設計する方法は知っていますが、これらの引数をコールバック関数に「再渡す」方法はわかりません。

具体例を挙げましょう。 というコールバック関数がaddToList(Element e, List list)あり、 ht_walk 宣言が になったとしますht_walk(HashTree tree, (*callback)(Element e), ...)。次のスニペットのように ht_walk を使用したいとします。

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);

これを行う方法はありますか?前もって感謝します!

4

5 に答える 5

3

これは、2つの方法のいずれかを使用して解決できます。

最も一般的で、理解しやすく、クリーンな方法は、「ユーザー」構造を使用することです。

void ht_walk(HashTree tree, void (*callback)(Element e, void *user), void *user);

void addToList(Element e, void *arg)
{
    STATIC_ASSERT(sizeof(void *) >= sizeof(List));

    List list = arg;

    /* ... */
}

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);

別の方法は受け入れることva_listです:

#include <stdarg.h>

void ht_walk(HashTree tree, void (*callback)(Element e, va_list args), ...)
{
    for(..)
    {
        va_list args;
        va_start(args, callback);
        callback(element, args);
        va_end(args);
    }
}

void addToList(Element e, va_list args)
{
    List list = va_arg(args, List);

    /* ... */
}

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);
于 2009-03-20T01:17:15.127 に答える
1

あなたがやりたいことは、任意の数の引数を渡すことではないと思います。ここにあるのは、複数のタイプのコールバックがある場合だと思います。あなたの例では、リストを渡しますが、要素を渡すこともあると言います。したがって、あなたが本当にやりたいことは、コールバックを宣言してvoid*とそれが何であるかを示すフラグを取ることだと私には思えます。何かのようなもの:

void callback(void* arg, int type) {
    switch (type) {
    case ARG_TYPE_LIST:
        List* list = (List*)arg;
        ...
        break;
    case ARG_TYPE_ELEMENT:
        Element* ele = (Element*)arg;
        ...
        break;
    ...
}

リストとして渡す場合、リストはカウントを知っている必要があるので、それを渡すことを心配する必要はありません。

または、複数のタイプのコールバックを定義し、実行する必要のある処理の種類に基づいて、常に適切なタイプのコールバックをコールバックすることもできます。

ストレートCに制限されていない場合(つまり、C ++を使用できる場合) 、 boost :: bindを使用して調査することができます。これは、この種のことに非常に役立つためです。

于 2009-03-20T01:17:47.247 に答える
1

可変引数 (va_start など) を処理するための一連の関数とマクロがあります。GNU には、ここに良いガイドがあります。

そうは言っても、一種の訪問者パターンを説明しているようです。ここでの可変引数リストの使用が好きかどうかはわかりません。通常、コールバックがすべてのノードに対して一貫して取得するパラメータを確立できない場合は、そもそも問題があります。

于 2009-03-20T01:11:33.113 に答える
0

va_listマクロを使っていると思います。stdarg のマニュアル ページを参照してください。

私がこれを試してからしばらく経ちましたが...

于 2009-03-20T01:10:32.007 に答える
0

コールバック関数で可変数の引数を使用できるとは思わないので、void * を渡します。私は間違っているかもしれません。

typedef void (*tree_walk_callback)(Element e, void *data);

add_element(Element e, void *data)
{
    List *list = (List *)data;
    add_element_to_list(list, e);
}

...in your function...
List my_list;
ht_walk(my_tree, &add_element, (void *)&my_list);

ユーザーが複数の引数を渡したい場合は、必要なものを含む構造体を渡します。

于 2009-03-20T01:11:09.107 に答える