Loading

在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

posted @ 2020-09-05 15:40  ink19  阅读(1236)  评论(2编辑  收藏  举报