新的解决方法
https://www.cnblogs.com/ink19/p/13768425.html
libuv简介
libuv是一个可以跨平台的C语言库,它提供了基于事件的异步IO支持[1]。提供了很多事件的支持,涉及到网络、文件、信号、线程、进程等。主要设计应用在Nodejs,也有很多其他知名的项目使用了这一库。
问题说明
libuv的易用性非常高(在我看来比boost.asio简单多了),如果用C来调用它的话,基本上没有什么问题。但是C++在构建大型项目上有着无可比拟的优异性。在我准备把我之前用C写的一个libuv程序[2]改成C++的时候,出现了一些问题。
问题在哪呢?如果有了解libuv的话,应该都知道在使用libuv的时候使用了大量的回调。如果使用C,那只要按照要求对每一个回调函数写一个相应的回调就好了,但是这种方法在C++里面并不优雅。我们更希望能够直接将回调函数写在类的内部,这样就可以直接对类的数据进行操作。为了实现这一想法,我很自然的依靠std::bind
写出了一下的代码。
class A {
public:
void on_callback(uv_req_t *req);
};
int main() {
A a;
uv_fs_open(loop, req, path, flags, mode, std::bind(&A::on_callback, &a, _1));
return 0;
}
但是很遗憾编译错误。uv_fs_open需要的是一个函数指针,但是std::bind提供的并不是一个指针,它还包含了很多的东西。比如A类的实例a。这导致了它没办法转换成函数指针。
解决方法
那有没有可靠的解决方法呢?我在网上找了很多的资料,其中轮子哥的一个方法[3]好像可以解决这个问题,但是由于才疏学浅,并不能读到那篇博文。还有一种方法是C++ thunk
它通过汇编编程解决了这一问题,不过对平台的兼容性不好。
为了完成自己的想法,我结合自己所学的一些C++知识完成了解决这一问题的办法。
构建回调测试
为了方便快捷,我们肯定不能直接使用libuv里面的函数进行回调测试,因此我自己写了一点代码,模拟了回调过程。
extern "C" {
typedef struct {
int value;
void *data;
}req_t;
typedef void (*call_t)(req_t *a, int b, double c);
int call_back_function(req_t *data, call_t func) {
data->value = 1023;
func(data, 1, 2.0);
return 0;
}
}
需要调用的回调函数及类
class A {
public:
int s = 10;
void true_call_back(req_t *t, int b, double c) {
std::cout << t->value << std::endl;
std::cout << s << std::endl;
}
};
整个过程就是call_back_function
调用A::true_call_back
完成回调过程。
解决方法
template
struct data_t{
T& t;
S call_it;
};
template
void common_call_back(S* req, Args... data) {
//std::cout << main_class->s << std::endl;
T* pdata = static_cast(req->data);
(pdata->t.*(pdata->call_it))(req, data...);
}
使用
int main() {
A a;
data_t data{a, &A::true_call_back};
req_t req;
req.data = (void *)&data;
call_back_function(&req, common_call_back);
}
简单来说,就是通过模板,为每一个回调产生一个函数。为了保持成员函数能够找到对应的类,使用了一个额外的结构体data_t
。
引用
[1] https://github.com/libuv/libuv
[2] https://github.com/ink19/ProgramRunTest
[3] https://zhuanlan.zhihu.com/p/23952898