13

コールバックのレビューを開始しました。SO でこのリンクを見つけました: What is a "callback" in C and how are implemented? 仕事で使用するものと非常によく似たコールバックの良い例があります。しかし、私はそれを機能させようとしましたが、多くのエラーがあります。

#include <stdio.h>

/* Is the actual function pointer? */
typedef void (*event_cb_t)(const struct event *evt, void *user_data);

struct event_cb
{
    event_cb_t cb;
    void *data;
};

int event_cb_register(event_ct_t cb, void *user_data);

static void my_event_cb(const struct event *evt, void *data)
{
    /* do some stuff */
}

int main(void)
{
    event_cb_register(my_event_cb, &my_custom_data);

    struct event_cb *callback;

    callback->cb(event, callback->data);

    return 0;
}

コールバックは関数ポインターを使用して関数のアドレスを格納することを知っています。しかし、私が理解していないと思うことがいくつかあります:

  • 「コールバックの登録」と「イベントディスパッチャ」とは何を意味しますか?
4

6 に答える 6

14

このコードは、-Wall を指定した GCC でコンパイルおよび実行されます。

#include <stdio.h>

struct event_cb;

typedef void (*event_cb_t)(const struct event_cb *evt, void *user_data);

struct event_cb
{
    event_cb_t cb;
    void *data;
};

static struct event_cb saved = { 0, 0 };

void event_cb_register(event_cb_t cb, void *user_data)
{
    saved.cb = cb;
    saved.data = user_data;
}

static void my_event_cb(const struct event_cb *evt, void *data)
{
    printf("in %s\n", __func__);
    printf("data1: %s\n", (const char *)data);
    printf("data2: %s\n", (const char *)evt->data);
}

int main(void)
{
    char my_custom_data[40] = "Hello!";
    event_cb_register(my_event_cb, my_custom_data);

    saved.cb(&saved, saved.data);

    return 0;
}

おそらく、コールバック関数が構造体 event_cb 全体を取得するかどうかを確認する必要があります。通常は、示されているように、同じ情報のソースが 2 つあるため (およびへのポインターの予備のコピーがあるため)、通常はデータを渡すだけです。あなたがいる機能)。これで実行できるクリーンアップはたくさんありますが、うまくいきます。


コメントでの質問: これはコールバックの良い例ですか?

簡潔に言うと、そうではありませんが、その理由の 1 つは、ここには十分なインフラストラクチャがないためです。

qsort()ある意味では、またはbsearch()関数に渡される比較関数はコールバックと考えることができます。これは、ジェネリック関数がそれ自体では実行できないことを実行する、ジェネリック関数に渡される関数へのポインターです。

コールバックのもう 1 つの例は、シグナル ハンドラ関数です。イベント (シグナル) が発生したときに関数を呼び出すようにシステムに指示します。システムが関数を呼び出す必要があるときに、どの関数を呼び出すかをシステムが認識できるように、事前にメカニズムをセットアップします。

サンプル コードは、より精巧なメカニズム (コンテキストを伴うコールバック) を提供しようとしています。C++ では、これはおそらくファンクタになります。

私が扱っているコードの中には、特定のコンテキストで使用する場合、メモリ管理に関して非常に面倒な要件があるものがあります。したがって、テストにはmalloc()et al を使用しますが、本番環境では、メモリ アロケーターを特殊なアロケーターに設定する必要があります。次に、パッケージに関数呼び出しを提供して、うるさいコードがデフォルトのメモリ アロケーターを独自のサロゲート バージョンでオーバーライドできるようにします。サロゲートが正常に機能する場合、コードは以前と同じように動作します。これらはコールバックの形式です - 繰り返しますが、ユーザー コンテキスト データの方法で多く (または何も) を必要としない形式です。

ウィンドウ システムには、登録されたイベント ハンドラー (コールバック) があり、イベントが発生したときに GUI メイン イベント ループが呼び出します。これらは通常、GUI システムによって提供されるイベント固有の情報だけでなく、ユーザー コンテキストも必要とします。

于 2009-03-10T17:12:28.537 に答える
7

「コールバックの登録」と「イベントディスパッチャ」とは何を意味しますか?

「コールバックを登録する」とは、基礎となるシステムに、呼び出す正確な関数、および (オプションで) どのパラメーターを使用するか、また場合によってはそのコールバックを呼び出す必要があるイベントの特定のクラスを通知する行為です。

「イベント ディスパッチャー」は、O/S (または GUI など) からイベントを受信し、登録されたコールバックのリストを調べて、そのイベントに関心のあるものを確認することにより、実際にコールバックを呼び出します。

于 2009-03-10T17:17:19.023 に答える
2

コンパイラの出力がなければ難しいですが、いくつか問題があります。

int event_cb_register(event_ct_t cb, void *user_data);

する必要があります

int event_cb_register(event_cb_t cb, void *user_data);

ここmy_custom_dataで使用されている場合、変数は存在しません。

event_cb_register(my_event_cb, &my_custom_data);

このポインターは初期化されません。

struct event_cb *callback;

そしてで;

callback->cb(event, callback->data);

型 ('event') の名前を関数に渡すことはできません。その型のインスタンスを渡す必要があります。

于 2009-03-10T17:09:02.320 に答える
2
int event_cb_register(event_ct_t cb, void *user_data);

そのタイプは何event_ct_tですか?ということevent_cb_tですか?

struct event_cb *callback;

構造体への初期化されていないポインターを作成しますevent_cb。ほとんどの場合、これはガベージを指していることに注意してください。

callback->cb(event, callback->data);

あなたはゴミを呼ぼうとしています。初期化が必要です:

struct event_cb callback;
callback.cb = my_event_cb;
callback.data = 42;

またはそのようなもの。

于 2009-03-10T17:13:06.460 に答える
1

コールバックを登録するということは、対象のイベントが発生したときに呼び出す関数を指定していることを意味します。基本的に、コールバックを登録するときに関数ポインタを設定しています。

于 2009-03-10T17:11:33.030 に答える
1

宣言した構造体のポインターを作成しましたが、何も指していません。

struct event_cb *callback;

構造体の型を作成するだけです。

struct event_cb callback;

次に、そのアドレスを関数に渡します。

于 2010-05-27T20:18:32.273 に答える