I am trying to wrap a library for porting purpose. The library exposes a function say -
fooLib(int , char , function pointer A);
signature of the function pointer A is
void handler(DataFormat);
where DataFormat
is a struct
I don't want my wrapper to expose this library's callback function. I want to create a different function that should be used by the consumers of my wrapper, say
int handlerNew(NewDataFormat);
where NewDataFormat
is my struct
The question now is how can I link these two functions? Whenever the library calls handler
I want it to call my callback handlerNew
after filling up the NewDataFormat
struct from the DataFormat
.
As long as you don't need thread safety, this is not hard. You just have to provide a private (static) handler with the library's interface that transforms the library data struct into your wrapped version, then calls your callback with that as an argument. Your interface will look like:
// wrapped_foo_lib.h
typedef struct { ... } NewDataFormat;
typedef void (*WRAPPED_CALLBACK)(NewDataFormat);
void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb);
Your implementation, which the client never gets to see is:
// 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);
}
The non-thread safety is why every API callback should include a void *
that you get to define, which is passed on to the callback. I.e. your furnished library should be defined as
fooLib(int, char, void (*)(DataFormat, void *env));
void handler(DataFormat, void *env);
Now when you call fooLib
, you furnish any struct at all as env
, and it's passed back to you. This way you can dispense with the static variable in the wrapper:
// 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);
}
This is thread safe because ENV
is stack allocated. A nice example of this done well is the libpng.
Feel free to update the C90 to more modern syntax.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With