2015-08-11 12 views
7

Sto tentando di racchiudere una libreria per scopi di porting. La biblioteca espone una funzione di dire -Puntatore funzione di avvolgimento

fooLib(int , char , function pointer A); 

firma del puntatore a funzione A è

void handler(DataFormat); 

dove DataFormat è una struct

io non voglio che il mio involucro per esporre funzione di callback di questa libreria . Voglio creare una funzione diversa che deve essere utilizzato dai consumatori del mio involucro, dico

int handlerNew(NewDataFormat); 

dove NewDataFormat è

La domanda mio struct ora è come posso collegare queste due funzioni? Ogni volta che la libreria chiama handler, desidero richiamare la mia richiamata handlerNew dopo aver riempito la struttura NewDataFormat dallo DataFormat.

+1

A volte la registrazione di callback accetta un 'void *' oltre al puntatore della funzione, quindi passa quel 'void *' alla funzione di callback. Ciò consente ai dati personalizzati di essere "collegati" al puntatore della funzione. Senza questa caratteristica, questo è molto difficile. –

+0

questo è vero, ma questa libreria non accetta alcun puntatore void *. – Saaras

+0

Non è chiaro come tutti i bit combacino. Come si usa 'fooLib'? Come/quando la libreria invoca il 'handler'? In che modo il codice client usa il wrapper: hai solo detto che il wrapper ha una funzione 'handlerNew' ma implica che questo sia chiamato dalla libreria e non dal codice client. Quindi, che cosa chiama il codice client: la libreria originale o il tuo wrapper? – kaylum

risposta

5

Finché non è necessaria la sicurezza del filo, questo non è difficile. Devi solo fornire un gestore (statico) privato con l'interfaccia della libreria che trasforma la struttura dei dati della libreria nella tua versione avvolta, quindi chiama la tua callback con quella come argomento. L'interfaccia sarà simile:

// wrapped_foo_lib.h 
typedef struct { ... } NewDataFormat; 
typedef void (*WRAPPED_CALLBACK)(NewDataFormat); 
void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb); 

L'implementazione, quali il cliente non viene mai vedere è:

sicurezza
// wrapped_foo_lib.c 
// This static var makes this module _not_ thread safe. 
static WRAPPED_CALLBACK wrapped_callback; 

static void private_handler(DataFormat data) { 
    NewDataFormat new_data = ...; // extract new_data from data 
    wrapped_callback(new_data); 
} 

void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb) { 
    wrapped_callback = cb;  
    foo_lib(x, c, private_handler); 
} 

La non-thread è il motivo per cui ogni API di callback dovrebbe includere una void * che si ottiene per definire, che viene passato al callback. Cioè la libreria arredato dovrebbe essere definito come

fooLib(int, char, void (*)(DataFormat, void *env)); 
void handler(DataFormat, void *env); 

Ora quando si chiama fooLib, arredi qualsiasi struct affatto come env, ed è passata di nuovo a voi. In questo modo è possibile fare a meno della variabile statica nella confezione:

// wrapped_foo_lib.c 
typedef struct { WRAPPED_CALLBACK wrapped_callback; } ENV; 

static void private_handler(DataFormat data, void *void_env) { 
    ENV *env = (ENV*)void_env; 
    NewDataFormat new_data = ...; // extract new_data from data 
    env->wrapped_callback(new_data); 
} 

void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb) { 
    ENV env[1] = {{ cb }};  
    foo_lib(x, c, env); 
} 

questo è thread sicura perché ENV viene allocato pila. Un buon esempio di questo fatto è il libpng.

Sentiti libero di aggiornare la C90 alla sintassi più moderna.

+1

In GCC almeno puoi aggirare il problema di sicurezza del thread cambiando 'static WRAPPED_CALLBACK wrapped_callback;' per '__thread WRAPPED_CALLBACK wrapped_callback;'. La variabile wrapped_callback è quindi una variabile globale del thread, quindi può essere impostata in modo sicuro per thread. – bazza

+0

Funziona, l'unica preoccupazione è se i miei clienti desiderano registrare più callback, quindi solo l'ultima callback registrata verrà ricordata dalla variabile statica. C'è qualche cosa intorno a questo. – Saaras

+0

@Saaras, wrappedFooLibCall potrebbe prendere una lista o una serie di puntatori di funzione di richiamata invece di un singolo puntatore. La variabile statica potrebbe essere una copia di quell'elenco e il gestore privato semplicemente eseguirà l'iterazione sull'elenco chiamando a turno ciascun gestore. – bazza