2015-08-11 10 views
7

Ich versuche, eine Bibliothek für die Portierung zu verpacken. Die Bibliothek stellt eine Funktion sagen -Wrapping-Funktionszeiger

fooLib(int , char , function pointer A); 

Unterschrift des Funktionszeiger A

void handler(DataFormat); 

wo DataFormat ist eine Struktur

Ich will nicht meine Wrapper Bibliothek Callback-Funktion belichten . Ich möchte eine andere Funktion erstellen, die von den Verbrauchern meiner Wrapper verwendet werden soll, sagen

int handlerNew(NewDataFormat); 

wo NewDataFormat meine Struktur ist

Die Frage ist nun, wie kann ich diese beiden Funktionen verknüpfen? Immer wenn die Bibliothek handler aufruft möchte ich, dass sie meinen Callback handlerNew aufruft, nachdem ich dieStruktur aus der DataFormat aufgefüllt habe.

+1

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. –

+0

das ist wahr, aber diese Bibliothek nimmt keinen void * Zeiger. – Saaras

+0

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

Antwort

5

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.

+1

In GCC zumindest können Sie umgehen die Thread Sicherheitsproblem durch Ändern der 'statische WRAPPED_CALLBACK wrapped_callback;' zu '__thread WRAPPED_CALLBACK wrapped_callback;'. Die Variable wrapped_callback ist dann eine Thread-globale Variable, also kann pro Thread sicher festgelegt werden. – bazza

+0

Dies funktioniert, die einzige Sorge ist, wenn meine Kunden mehrere Callbacks registrieren möchten, dann nur der letzte registrierte Rückruf wird von der statischen Variable erinnert werden. Gibt es da welche? – Saaras

+0

@Saaras, wrappedFooLibCall könnte eine Liste oder ein Array von Funktionszeigern für Rückrufe anstelle eines einzelnen Zeigers enthalten. Die statische Variable könnte eine Kopie dieser Liste sein, und der private Handler würde einfach über die Liste iterieren und jeden Handler der Reihe nach aufrufen. – bazza