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.
Perché non includere i tuoi errori? –