我使用 std::bind 提供回调,同时通过首先绑定(bind)一些参数来抽象一些逻辑。即
void start() {
int secret_id = 43534;
//Bind the secret_id to the callback function object
std::function<void(std::string)> cb = std::bind(&callback, secret_id, std::placeholders::_1);
do_action(cb);
}
void do_action(std::function<void(std::string)> cb) {
std::string result = "hello world";
//Do some things...
//Call the callback
cb(result);
}
void callback(int secret_id, std::string result) {
//Callback can now do something with the result and secret_id
}
所以在上面的例子中,do_action 不需要知道 secret_id 并且其他函数可以在没有自己的 secret_id 的情况下重用它。这在 do_action 是某种异步操作时特别有用。
我的问题是,有没有办法仅使用 C 将参数值绑定(bind)到函数指针?
如果不是通过模拟 std::bind 那么是否有另一种方法可以将数据从 first() 传递到 callback() 而不会使中性的 do_action() 复杂化?
最佳答案
没有。 C 不允许您直接这样做。
在 C 中,处理回调的标准方法是使用上下文指针:
void register_callback(void (*cback)(void *context, int data),
void *context);
这意味着除了回调应该处理的正常参数(在上述情况下是一个整数)之外,您还将传递一个接受 void *
的函数,并且您还将传递一个 void *
你想被传回。
这个 void *
通常指向一个 struct
,它将包含你在回调中需要的所有额外参数或数据,并且使用这种方法库不依赖于这个上下文是什么。如果回调不需要任何上下文,您只需将 NULL 指针作为 context
传递,并在从库中调用时忽略第一个参数。
如果上下文是适合 void *
大小的简单数据(例如整数)并且您的环境是不会有问题你可以通过传递一个伪造的 void *
来欺骗库分配上下文并管理其生命周期)。
关于如何欺骗语言来避免这种限制(仍然留在可移植 C 的土地上)我可以想到一些 hack:
首先我们分配一个双参数回调和上下文数据池
void (*cbf[6])(int, int);
int ctx[6];
然后我们编写(或宏生成)我们希望注册的函数,该函数将调用双参数版本。
void call_with_0(int x) { cbf[0](ctx[0], x); }
void call_with_1(int x) { cbf[1](ctx[1], x); }
void call_with_2(int x) { cbf[2](ctx[2], x); }
void call_with_3(int x) { cbf[3](ctx[3], x); }
void call_with_4(int x) { cbf[4](ctx[4], x); }
void call_with_5(int x) { cbf[5](ctx[5], x); }
我们还将它们存储在分配和释放它们的池中:
int first_free_cback = 0;
int next_free_cback[6] = {1, 2, 3, 4, 5, -1};
void (*cbacks[6])(int) = { call_with_0,
call_with_1,
call_with_2,
call_with_3,
call_with_4,
call_with_5 };
然后为了绑定(bind)第一个参数,我们可以做类似的事情
void (*bind(void (*g)(int, int), int v0))(int)
{
if (first_free_cback == -1) return NULL;
int i = first_free_cback;
first_free_cback = next_free_cback[i];
cbf[i] = g; ctx[i] = v0;
return cbacks[i];
}
但绑定(bind)函数也必须显式释放
int deallocate_bound_cback(void (*f)(int))
{
for (int i=0; i<6; i++) {
if (f == cbacks[i]) {
next_free_cback[i] = first_free_cback;
first_free_cback = i;
return 1;
}
}
return 0;
}
关于c++ - 在 C 中模拟 std::bind,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19046028/