このコードは、-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 システムによって提供されるイベント固有の情報だけでなく、ユーザー コンテキストも必要とします。