只要不需要线程安全,这并不难。您只需提供一个私有(静态)处理程序,用于将库数据结构转换为您的包装版本,然后将其作为参数调用您的回调函数。您的接口应如下所示:
typedef struct { ... } NewDataFormat;
typedef void (*WRAPPED_CALLBACK)(NewDataFormat);
void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb);
你的实现,客户端永远无法看到:
static WRAPPED_CALLBACK wrapped_callback;
static void private_handler(DataFormat data) {
NewDataFormat new_data = ...;
wrapped_callback(new_data);
}
void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb) {
wrapped_callback = cb;
foo_lib(x, c, private_handler);
}
非线程安全性是为什么每个API回调都应该包括一个由您定义且传递给回调的
void *
的原因。也就是说,你提供的库
应该定义为:
fooLib(int, char, void (*)(DataFormat, void *env));
void handler(DataFormat, void *env);
现在当您调用
fooLib
时,您可以提供任何结构作为
env
,它将传递回给您。这样您就可以省去包装器中的静态变量:
typedef struct { WRAPPED_CALLBACK wrapped_callback; } ENV;
static void private_handler(DataFormat data, void *void_env) {
ENV *env = (ENV*)void_env;
NewDataFormat new_data = ...;
env->wrapped_callback(new_data);
}
void wrappedFooLibCall(int x, char c, WRAPPED_CALLBACK cb) {
ENV env[1] = {{ cb }};
foo_lib(x, c, env);
}
这是线程安全的,因为
ENV
是堆栈分配的。好的例子就是libpng。
可以随意将C90更新为更现代的语法。
void*
,并将该void*
传递给回调函数。这样可以将自定义数据“附加”到函数指针上。如果没有此功能,这将非常困难。 - John KugelmanfooLib
库怎么用?库什么时候调用handler
函数?客户端代码如何使用你的包装器 - 你只说了包装器有一个handlerNew
函数,但暗示这个函数是由库而不是客户端代码调用的。那么客户端代码调用什么 - 原始库还是你的包装器? - kaylumextern“C”{void realHandler(NewDataFOrmat);}
),那么问题不大,但在其他语言中可能会更加棘手。 - bazza