C の関数ポインターとコールバック

このコードは、-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 つのソース (およびへのポインターの予備のコピー) があるためです。あなたがいる機能)。これに対して実行できるクリーンアップはたくさんありますが、うまくいきます。

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

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

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

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

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

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

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


「コールバックを登録する」とは、基礎となるシステムに対して、呼び出す正確な関数と (オプションで) どのパラメーターを指定するか、また場合によってはそのコールバックを呼び出すイベントの特定のクラスを指定することです。

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


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

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') の名前を関数に渡すことはできません。そのタイプのインスタンスを渡す必要があります。