新的解決方法
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<typename T, typename S>
struct data_t{
T& t;
S call_it;
};
template<typename S, typename T, typename ... Args>
void common_call_back(S* req, Args... data) {
//std::cout << main_class->s << std::endl;
T* pdata = static_cast<T*>(req->data);
(pdata->t.*(pdata->call_it))(req, data...);
}
使用
int main() {
A a;
data_t<A, decltype(&A::true_call_back)> data{a, &A::true_call_back};
req_t req;
req.data = (void *)&data;
call_back_function(&req, common_call_back<req_t, decltype(data), int, double>);
}
簡單來說,就是通過模板,為每一個回調產生一個函數。為了保持成員函數能夠找到對應的類,使用了一個額外的結構體data_t
。
引用
[1] https://github.com/libuv/libuv
[2] https://github.com/ink19/ProgramRunTest
[3] https://zhuanlan.zhihu.com/p/23952898