在C++中使用libuv时对回调的处理 (2)

前情简介

在完成了第一版的《在C++中使用libuv时对回调的处理》之后,在对项目进行开发的时候,还是感觉有一些难受。

因为在实际操作的时候,需要构建一个结构体,并且需要对这个结构体的内存进行管理,非常的麻烦。

在对C++的模板编程进行简单的学习后,了解到一个比较基本的知识。如果一个值或者类型能在编译的时候确定,那么它是一定可以作为模板参数的。

反观我之前为了完成操作构建的结构体,可以很明显的发现,成员函数指针那一个变量是一直保持不变的,而且可以在编译的时候确定,所以是有办法将成员函数指针放入模板里面的。

解决方案

使用回调的函数

typedef struct {
    void *data;
    int s;
} call_back_t;

typedef void (*call_back_func_t)(call_back_t *t, int s, int v);

int call_back_func(call_back_t *t, call_back_func_t func) {
    func(t, t->s, 12);
    return 0;
}

回调函数及其类

class CallBack {
public:
    int a = 0;
    void call_back(call_back_t *t, int s, int v) {
        std::cout << "t->s:" << t->s << std::endl;
        std::cout << "s:" << s << std::endl;
        std::cout << "v:" << v << std::endl;
        std::cout << "a:" << a << std::endl; 
    }
};

解决方案

template<typename T,  T>
struct comm_call_back_s;

template<typename ClassType, typename ...ArgTypes, void (ClassType::*FunType)(call_back_t *t, ArgTypes...) >
struct comm_call_back_s<void (ClassType::*)(call_back_t *t, ArgTypes...), FunType> {
    static void comm_call_back(call_back_t *t, ArgTypes... Value) {
        ClassType *mClass = static_cast<ClassType *>(t->data);
        (mClass->*FunType)(t, std::forward<ArgTypes>(Value)...);
    }
};

#define define_comm_call_back_s(F) (comm_call_back_s<decltype((F)), (F)>::comm_call_back)

以上代码是根据[1]中大佬代码修改得来的。首先是第一段

template<typename T,  T>
struct comm_call_back_s;

这一段代码定义了模板的原型,模板包括两个参数。一个是类型T,另一个是类型为T的值。

template<typename ClassType, typename ...ArgTypes, void (ClassType::*FunType)(call_back_t *t, ArgTypes...) >
struct comm_call_back_s<void (ClassType::*)(call_back_t *t, ArgTypes...), FunType> {
    static void comm_call_back(call_back_t *t, ArgTypes... Value) {
        ClassType *mClass = static_cast<ClassType *>(t->data);
        (mClass->*FunType)(t, std::forward<ArgTypes>(Value)...);
    }
};

第二段代码是我们主要使用的偏特化模板。一共定义了三个模板参数,第一个是包含回调函数的类,第二个是回调函数的部分参数,第三个是回调函数。

在具体的特化中,我们将回调函数的类型作为原型里面的类型T,回调函数作为值。

然后,我们定义了一个comm_call_back函数作为我们封装的回调函数。

#define define_comm_call_back_s(F) (comm_call_back_s<decltype((F)), (F)>::comm_call_back)

最后一段,我们定义了一个宏,方便我们的调用。

使用

int main() {
    CallBack b;
    b.a = 100;
    call_back_t call;
    call.s = 1024;
    call.data = static_cast<void *>(&b);
    call_back_func(&call, define_comm_call_back_s(&CallBack::call_back));
}

完整代码示范

反思

在写完这一些代码后,我思考了几个问题,并做了一定的解答。

  1. 为什么使用结构体,而不直接使用模板函数。
    因为我们在定义模板原型的时候没办法决定函数的参数,所以先使用结构体定义,然后使用偏特化实现具体的函数。

引用

[1] https://*.com/questions/9779105/generic-member-function-pointer-as-a-template-parameter

上一篇:POSTGRESQL数据库报: FATAL: no pg_hba.conf entry for host 的解决方案


下一篇:【OS操作系统】Operating System 第六章:页面置换算法