在C++中使用libuv時對回調的處理


新的解決方法

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM