2009-03-10 9 views
11

Ich habe begonnen, Callbacks zu überprüfen. Ich fand diesen Link auf SO: What is a "callback" in C and how are they implemented? Es hat ein gutes Beispiel für Rückruf, die sehr ähnlich ist, was wir bei der Arbeit verwenden. Ich habe jedoch versucht, es zur Arbeit zu bringen, aber ich habe viele Fehler.Funktionszeiger und Callbacks in C

#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; 
} 

Ich weiß, dass Callbacks Funktionszeiger verwenden, um eine Adresse einer Funktion zu speichern. Aber es gibt ein paar Dinge, die ich nicht verstehe:

  • Was bedeutet "Registrierung des Rückrufs" und "Event Dispatcher"?
+0

Warum nicht Ihre Fehler einschließen? –

Antwort

12

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.

+0

Und der Code stürzt fürchterlich ab, wenn Sie die Funktion nicht registrieren. –

+0

Hallo, Danke für Ihre Mühe. Ich komme gerade in Funktionszeiger, da ich sie zum ersten Mal bei der Arbeit verwenden muss. Ist das ein gutes Beispiel für einen Rückruf? Das Beispiel, das ich dir gegeben habe, kam uns nahe. Wenn Sie etwas besseres wissen? – ant2009

+0

Nicht klar. Wer ist "gerettet"? Wo ist es in deinem Code definiert? – Curnelious

2

Ohne den Compiler-Ausgang ist es schwer, aber ich sehe ein paar Probleme;

int event_cb_register(event_ct_t cb, void *user_data); 

Sollte

int event_cb_register(event_cb_t cb, void *user_data); 

sein Die my_custom_data Variable nicht existiert, wenn es hier verwendet wird;

event_cb_register(my_event_cb, &my_custom_data); 

Dieser Zeiger wird nie initialisiert;

struct event_cb *callback; 

Und in;

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

Sie nicht den Namen eines Typs passieren kann (‚Event‘) auf eine Funktion, müssen Sie eine Instanz dieses Typs übergeben.

+0

Der erste Parameter ist event_ct_t, sollte aber event_cb_t sein. –

1

Das Registrieren eines Rückrufs bedeutet, dass Sie angeben, welche Funktion aufgerufen werden soll, wenn das Ereignis von Interesse auftritt. Grundsätzlich setzen Sie den Funktionszeiger beim Registrieren eines Callbacks.

2
int event_cb_register(event_ct_t cb, void *user_data); 

Was ist dieser Typ event_ct_t? Meinst du event_cb_t?

struct event_cb *callback; 

Erzeugt einen nicht initialisierten Zeiger auf eine Struktur event_cb. Beachte das meistens auf Müll.

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

Sie versuchen, Müll zu nennen. Sie brauchen die Initialisierung:

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

oder einige solche Sachen.

6

Was passiert mit "Registrierung des Callbacks" und "Event Dispatcher"?

„den Rückruf Registrierung“ ist der Akt der sage die underyling System, das eine präzise Funktion aufzurufen, sowie (optional) mit dem Parameter, und möglicherweise auch, für die bestimmte Klasse von Ereignissen, die Rückruf aufgerufen werden sollte.

Der "Event Dispatcher" empfängt Ereignisse vom O/S (oder GUI, etc) und ruft die Callbacks auf, indem er in einer Liste von Callbacks nachschlägt, welche an diesem Event interessiert sind.

+1

hallo, saved.cb (& gespeichert, gespeicherte.data); Wäre das der Event Depatcher, der den Calback aufruft? Also Registrierung der Callback würde die Adresse der Callback-Funktion dem Funktionszeiger zuweisen, die den Callback aufrufen, wenn der Funktionszeiger aufgerufen wird? – ant2009

+0

Ja, das ist der Dispatcher, wenn auch der trivialste. – Alnitak

1

Sie erstellt einen Zeiger der Struktur Sie erklärt, aber es alles zeigt nicht:

struct event_cb *callback; 

Sie sollten nur eine Art Ihrer Struktur erstellen:

struct event_cb callback; 

und dann passieren seine Adresse zu den Funktionen.