Solange Sie keine Fadensicherheit benötigen, ist das nicht schwer. Sie müssen lediglich einen privaten (statischen) Handler mit der Schnittstelle der Bibliothek bereitstellen, die die Bibliotheksdatenstruktur in Ihre umbrochene Version umwandelt und dann Ihren Callback damit als Argument aufruft. Ihre Schnittstelle wird wie folgt aussehen:
// wrapped_foo_lib.h
typedef struct { ... } NewDataFormat;
typedef void (*WRAPPED_CALLBACK)(NewDataFormat);
void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb);
Ihre Implementierung, die der Kunde nie zu sehen bekommt, ist:
// 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);
}
Der nicht-Thread-Sicherheit ist, warum jeder API-Rückruf ein void *
enthalten sollte, die Sie erhalten zu definieren, die an den Rückruf weitergegeben wird. I.e. Ihre eingerichtete Bibliothek sollte als
fooLib(int, char, void (*)(DataFormat, void *env));
void handler(DataFormat, void *env);
nun definiert werden, wenn Sie fooLib
nennen Sie jede Struktur überhaupt als env
liefern, und es wird wieder an Sie weitergegeben. So können Sie mit den statischen Variablen in der Wrapper verzichten können:
// 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);
}
Dies ist Thread-sicher, da ENV
Stapel zugeordnet ist. Ein schönes Beispiel dafür ist die libpng.
Fühlen Sie sich frei, die C90 zu moderneren Syntax zu aktualisieren.
In einigen Fällen akzeptiert die Callback-Registrierung zusätzlich zum Funktionszeiger ein 'void *' und übergibt das 'void * 'dann an die Callback-Funktion. Dadurch können benutzerdefinierte Daten an den Funktionszeiger "angehängt" werden. Ohne dieses Merkmal ist dies sehr schwierig. –
das ist wahr, aber diese Bibliothek nimmt keinen void * Zeiger. – Saaras
Es ist ein bisschen unklar, wie alle Bits zusammenpassen. Wie wird 'fooLib' verwendet? Wie/wann ruft die Bibliothek 'Handler' auf? Wie verwendet der Client-Code Ihren Wrapper - Sie haben nur gesagt, dass der Wrapper eine "handlerNew" -Funktion hat, aber impliziert, dass dieser von der Bibliothek und nicht vom Client-Code aufgerufen wird. Also, was ruft der Client-Code auf - die Original-Bibliothek oder Ihr Wrapper? – kaylum