我正在使用std :: 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将参数值绑定到函数指针?
如果不是通过模拟std :: bind那么还有另一种方法将数据从first()传递给callback()而不会使中性do_action()复杂化吗?
解决方法
C号不允许您直接执行此操作.
在C中,处理回调的标准方法是使用上下文指针:
void register_callback(void (*cback)(void *context,int data),void *context);
这意味着除了回调应该处理的普通参数(在上面的例子中是一个整数)之外,你将传递一个接受void *的函数,你还将传递一个你想要传回的void *.
此void *通常指向一个结构,该结构将包含回调中所需的所有额外参数或数据,并且使用此方法,库不依赖于此上下文的内容.如果回调不需要任何上下文,则只需将NULL指针作为上下文传递,并在从库中调用时忽略第一个参数.
有点像hackish和形式上不安全的东西,但有时会做的是,如果上下文是一个符合void *(例如整数)大小的简单数据,如果你的环境不会有问题,你可以欺骗通过传递一个假的void *只是一个整数,并在从库中调用时将它转换回整数(这可以节省调用者分配上下文并管理其生命周期).
关于如何欺骗语言以避免这种限制(仍然留在便携式C的领域)我可以想到一些黑客:
首先,我们分配一个双参数回调和上下文数据池
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 };
然后绑定第一个参数我们可以做类似的事情
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]; }
但必须明确释放绑定函数
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; }