Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapping function pointer

Tags:

c

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.

like image 284
Saaras Avatar asked Aug 11 '15 03:08

Saaras


1 Answers

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.

like image 187
Gene Avatar answered Oct 01 '22 00:10

Gene