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.
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. –
questo è vero, ma questa libreria non accetta alcun puntatore void *. – Saaras
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