Dieser Code wird kompiliert und läuft unter GCC mit -Wall.
#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;
}
Sie müssen sich vermutlich überprüfen, ob der Rückruffunktion die gesamte Struktur event_cb oder nicht bekommt - in der Regel, Sie würden nur die Daten übergeben, da, wie gezeigt, sonst hat man zwei Quellen der gleichen Informationen (und eine Ersatzkopie des Zeigers auf die Funktion, in der Sie sich befinden). Es gibt eine Menge Aufräumarbeiten, die damit gemacht werden können - aber es funktioniert.
Eine Frage in den Kommentaren fragt: Ist das ein gutes Beispiel für einen Rückruf?
Kurz, nein - aber teilweise, weil hier keine ausreichende Infrastruktur vorhanden ist.
In gewisser Weise können Sie sich die Vergleichsfunktion vorstellen, die an die Funktionen qsort()
oder bsearch()
als Rückruf übergeben wird. Es ist ein Zeiger auf eine Funktion, die an die generische Funktion übergeben wird, die das tut, was die generische Funktion nicht für sich selbst tun kann.
Ein anderes Beispiel für einen Callback ist eine Signal-Handler-Funktion. Sie weisen das System an, Ihre Funktion aufzurufen, wenn das Ereignis - ein Signal - auftritt. Sie richten die Mechanismen im Voraus ein, damit das System, wenn es eine Funktion aufrufen muss, weiß, welche Funktion aufgerufen werden soll.
Der Beispielcode versucht, einen komplizierteren Mechanismus bereitzustellen - einen Rückruf mit einem Kontext. In C++ wäre das vielleicht ein Funktor.
Einige der Code, mit denen ich arbeite, hat sehr pingelige Anforderungen an die Speicherverwaltung - wenn in einem bestimmten Kontext verwendet. Zum Testen verwende ich malloc()
und so weiter, aber in der Produktion muss ich die Speicherzuordner den spezialisierten Zuordnern zuweisen. Dann stelle ich einen Funktionsaufruf in dem Paket bereit, so dass der überflüssige Code die Standardspeicherzuordner mit seinen eigenen Ersatzversionen überschreiben kann - und vorausgesetzt, dass die Ersatzzeichen OK sind, verhält sich der Code wie zuvor. Dies ist eine Form des Rückrufs - wiederum ein Formular, das die Benutzerkontextdaten nicht sehr (oder gar nicht) benötigt.
Windowing-Systeme haben Event-Handler (Callbacks), die registriert sind und die GUI-Main-Event-Schleife aufruft, wenn Ereignisse auftreten. Diese benötigen normalerweise den Benutzerkontext sowie die ereignisspezifischen Informationen, die vom GUI-System bereitgestellt werden.
Warum nicht Ihre Fehler einschließen? –