No. C non consente di farlo direttamente.
In C il modo standard per gestire i callback sta usando puntatori di contesto:
void register_callback(void (*cback)(void *context, int data),
void *context);
questo significa che si passa una funzione che accetta una void *
in aggiunta ai normali parametri che la callback dovrebbe gestire (in il caso sopra è un intero) e passerai anche un void *
che vuoi essere passato indietro.
Questo void *
punti normalmente ad un struct
che conterrà tutti i parametri aggiuntivi o dati che vi interessano nella richiamata e utilizzando questo approccio la libreria non dipende da ciò che questo contesto è. Se il callback non ha bisogno di alcun contesto, basta passare un puntatore NULL come context
e ignorare il primo parametro quando viene chiamato dalla libreria.
Qualcosa che è una specie di hacker e formalmente non sicuro, ma a volte è fatto è che se il contesto è un dato semplice che si adatta alle dimensioni di un void *
(ad esempio, un intero) e se l'ambiente non sta andando ad avere problemi con esso puoi ingannare la libreria passando un falso void *
che è solo un numero intero e convertirlo in un numero intero quando viene chiamato dalla libreria (ciò evita al chiamante di allocare il contesto e di gestirne la durata).
su come come per ingannare la lingua per evitare questa limitazione (rimanendo nel paese di portatile C) mi viene in mente qualche trucco:
In primo luogo abbiamo allocare un pool di due argomenti callback e dei dati di contesto
void (*cbf[6])(int, int);
int ctx[6];
allora scriviamo (o macro-generano) funzioni che vogliamo registrare e che chiamerà le versioni due argomenti.
void call_with_0(int x) { cbf[0](ctx[0], x); }
void call_with_1(int x) { cbf[1](ctx[1], x); }
void call_with_2(int x) { cbf[2](ctx[2], x); }
void call_with_3(int x) { cbf[3](ctx[3], x); }
void call_with_4(int x) { cbf[4](ctx[4], x); }
void call_with_5(int x) { cbf[5](ctx[5], x); }
Noi li negozio anche in una piscina dove sono allocate e deallocate:
int first_free_cback = 0;
int next_free_cback[6] = {1, 2, 3, 4, 5, -1};
void (*cbacks[6])(int) = { call_with_0,
call_with_1,
call_with_2,
call_with_3,
call_with_4,
call_with_5 };
Poi di impegnare la primo parametro che possiamo fare qualcosa di simile
void (*bind(void (*g)(int, int), int v0))(int)
{
if (first_free_cback == -1) return NULL;
int i = first_free_cback;
first_free_cback = next_free_cback[i];
cbf[i] = g; ctx[i] = v0;
return cbacks[i];
}
ma rilegati funzioni deve anche essere esplicitamente deallocato
int deallocate_bound_cback(void (*f)(int))
{
for (int i=0; i<6; i++) {
if (f == cbacks[i]) {
next_free_cback[i] = first_free_cback;
first_free_cback = i;
return 1;
}
}
return 0;
}
Le chiusure sono davvero il modo per ottenere questo in "C". LibFFI fornisce chiusure ed è portabile alla maggior parte dei principali sistemi operativi: http://sourceware.org/libffi/ –
@BojanNikolic Buon punto, e libffi è molto più popolare del trampolino. Potrebbe essere apprezzata una risposta con un esempio che risolva il problema dell'OP usando libffi. – user4815162342