我试图包装一个库用于移植目的.图书馆公开了一个功能说 –
fooLib(int,char,function pointer A);
函数指针A的签名是
void handler(DataFormat);
其中DataFormat是一个结构
我不希望我的包装器公开这个库的回调函数.我想创建一个应该由我的包装器的使用者使用的不同功能
int handlerNew(NewDataFormat);
其中NewDataFormat是我的结构
现在的问题是如何将这两个功能联系起来?每当库调用处理程序时,我希望它在从DataFormat填充NewDataFormat结构后调用我的回调handlerNew.
解决方法
只要您不需要线程安全,这并不难.您只需提供一个私有(静态)处理程序和库的接口,该接口将库数据结构转换为您的包装版本,然后使用它作为参数调用您的回调.您的界面将如下所示:
// wrapped_foo_lib.h typedef struct { ... } NewDataFormat; typedef void (*WRAPPED_CALLBACK)(NewDataFormat); void wrappedFooLibCall(int x,char c,WRAPPED_CALLBACK cb);
您的实现,客户永远不会看到:
// 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,WRAPPED_CALLBACK cb) { wrapped_callback = cb; foo_lib(x,c,private_handler); }
非线程安全性是每个API回调应该包含您要定义的void *的原因,它将传递给回调.即您的家具图书馆应定义为
fooLib(int,void (*)(DataFormat,void *env)); void handler(DataFormat,void *env);
现在当你调用fooLib时,你提供任何结构作为env,它会传回给你.这样你可以省去包装器中的静态变量:
// 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,WRAPPED_CALLBACK cb) { ENV env[1] = {{ cb }}; foo_lib(x,env); }
这是线程安全的,因为ENV是堆栈分配的.一个很好的例子就是libpng.
随意将C90更新为更现代的语法.