2009-03-10 11 views
11

Ho iniziato a rivedere i callback. Ho trovato questo collegamento su SO: What is a "callback" in C and how are they implemented? Ha un buon esempio di callback che è molto simile a quello che usiamo al lavoro. Tuttavia, ho cercato di farlo funzionare, ma ho molti errori.Funzione di richiamo e richiamata 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; 
} 

So che i callback utilizzano i puntatori di funzione per memorizzare un indirizzo di una funzione. Ma ci sono alcune cose che trovo non capisco:

  • Cosa si intende per "la registrazione del callback" e "dispatcher di eventi"?
+0

Perché non includere i tuoi errori? –

risposta

12

Questo codice viene compilato ed eseguito in GCC con -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; 
} 

probabilmente è necessario verificare se la funzione di chiamata indietro ottiene l'intera struct event_cb o no - di solito, si era appena passare i dati, perché, come dimostrato, altrimenti si hanno due fonti della stessa informazione (e una copia di riserva del puntatore alla funzione in cui ti trovi). Su questo si può fare molta pulizia, ma funziona.


Una domanda nei commenti chiede: è un buon esempio di callback?

In modo succinto, no, ma in parte perché qui non ci sono infrastrutture sufficienti.

In un certo senso, è possibile pensare alla funzione di confronto passata alle funzioni qsort() o bsearch() come richiamata. È un puntatore a una funzione passata nella funzione generica che fa ciò che la funzione generica non può fare da sé.

Un altro esempio di callback è una funzione di gestione del segnale. Dite al sistema di chiamare la vostra funzione quando si verifica l'evento - un segnale. I meccanismi vengono impostati in anticipo in modo che quando il sistema deve chiamare una funzione, sappia quale funzione chiamare.

Il codice di esempio sta tentando di fornire un meccanismo più elaborato - una richiamata con un contesto. In C++, questo potrebbe essere un funtore.

Parte del codice con cui lavoro ha requisiti molto esigenti in merito alla gestione della memoria, se utilizzata in un contesto particolare. Quindi, per i test, io uso malloc() et al, ma in produzione, devo impostare gli allocatori di memoria agli allocatori specializzati. Quindi fornisco una chiamata di funzione nel pacchetto in modo che il codice pignolo possa sovrascrivere gli allocatori di memoria predefiniti con le proprie versioni sostitutive e, a condizione che i surrogati funzionino correttamente, il codice si comporterà come prima. Quelle sono una forma di callback - di nuovo, una forma che non ha bisogno di molto (o nulla) nel modo dei dati di contesto dell'utente.

I sistemi di generazione hanno gestori di eventi (callback) che sono registrati e che il ciclo di eventi principale della GUI chiamerà quando si verificano eventi. Di solito, sono necessari il contesto utente e le informazioni specifiche sull'evento fornite dal sistema GUI.

+0

E il codice si arresta in modo anomalo se non si registra la funzione. –

+0

Ciao, Grazie per il tuo impegno. Sto solo entrando nei puntatori di funzione, poiché dovrò usarli al lavoro per la prima volta. È un buon esempio di callback. L'esempio che ti ho dato sembrava vicino a quello che usiamo. Se sai qualcosa di meglio? – ant2009

+0

Non chiaro. Chi è "salvato"? dove è definito nel tuo codice? – Curnelious

2

Senza l'output del compilatore è difficile, ma vedo alcuni problemi;

int event_cb_register(event_ct_t cb, void *user_data); 

Dovrebbe essere

int event_cb_register(event_cb_t cb, void *user_data); 

La variabile my_custom_data non esiste quando viene utilizzato qui;

event_cb_register(my_event_cb, &my_custom_data); 

Questo puntatore non viene mai inizializzato;

struct event_cb *callback; 

E in;

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

Non è possibile passare il nome di un tipo ('evento') a una funzione, è necessario passare un'istanza di quel tipo.

+0

Il primo parametro è event_ct_t ma dovrebbe essere event_cb_t –

1

La registrazione di una richiamata indica che si sta specificando quale funzione deve essere chiamata quando si verifica l'evento di interesse. Fondamentalmente stai impostando il puntatore alla funzione quando registri un callback.

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

Che cos'è questo tipo event_ct_t? Intendi event_cb_t?

struct event_cb *callback; 

Crea un puntatore non inizializzato a una struttura event_cb. Nota soprattutto questo punta alla spazzatura.

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

Si sta tentando di chiamare spazzatura. È necessario l'inizializzazione:

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

o roba del genere.

6

Qual è incontrano per "la registrazione del callback" e "dispatcher di eventi"?

"registrandosi al callback" è l'atto di risposta del sistema underyling che precisa funzione di chiamata, e (opzionalmente) con i parametri, ed eventualmente anche per le quali particolare classe di eventi che callback deve essere richiamato.

Il "dispatcher di eventi" riceve eventi dall'O/S (o dalla GUI, ecc.) E richiama effettivamente i callback, cercando in un elenco di callback per vedere quali sono interessati a quell'evento.

+1

hello, saved.cb (& saved, saved.data); Questo sarebbe il depatcher dell'evento che invoca il punto? Quindi la registrazione del callback assegnerebbe l'indirizzo della funzione di callback al puntatore della funzione, che chiamerà il callback quando viene richiamato il puntatore della funzione? – ant2009

+0

sì, quello è il dispatcher, anche se il più banale possibile. – Alnitak

1

è stato creato un puntatore del struct avete dichiarato, ma non punta a nulla:

struct event_cb *callback; 

Si dovrebbe solo creare un tipo di vostra struct:

struct event_cb callback; 

e poi passare il suo indirizzo alle funzioni.