将接受子类指针的回调函数统一管理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将接受子类指针的回调函数统一管理相关的知识,希望对你有一定的参考价值。

假设一个场景:收到网络协议的时候自动调用对应回调函数,回调函数的对象是协议参数。当我们处理不同的协议的时候需要不同的参数,那么我们只能让具体的参数继承自一个基类A。

这样一来我们就只能这样写回调函数

void fun(A* p)
{
    具体类型* param = (具体类型*)p;
    ……
}

这样一来回调函数的内部一般第一句话就是强转类型(或者用dynamic_cast也可以),与逻辑无关。

如果遇到了我这样有强迫症的人会不爽,怎么解决掉这个问题?

//假设已经有B类继承自A类
class Manager { public: using CallbackFunction = std::function<void(A*)>; template <class T> void Register(std::string key, std::function<void(T*)> callback) { static_assert(std::is_base_of<A, T>::value); _functions.insert(std::make_pair(key, [=](A* p) { callback((T*)p); })); } void Trigger(std::string key, A* param) { auto it = _functions.find(key); if (it != _functions.end()) it->second(param); } private: std::map<std::string, CallbackFunction> _functions; }; int main() { Manager m; auto cb = [](B* p) { std::cout << "in cb" << std::endl; }; m.Register<B>("B", cb); B* temp = new B; m.Trigger("B", temp); delete temp; return 0; }

 

因为使用了static_assert所以需要在编译选项中加入/std:c++latest

先讲讲static_assert,static_assert是静态断言,可以在编译期间对一些常量表达式之类的计算来实现断言,没有运行时期开销。

std::is_base_of定义于<type_traits>中,功能是判断一个类是否为一个类的子类,具体的判断方法是SFINA(匹配失败不是错误),用模板的方法判断是否具有继承关系。

 

实现这个功能的核心思路就是利用模板成员函数接受子类对象类型的回调函数,然后套上一个lambda,在lambda中强转基类类型然后调用接受子类对象类型的回调函数。

不过说实话,上面这段代码学习意义大于实际意义,因为一般情况下这种回调函数框架都是由代码生成器生成的(什么你告诉我你手写?再见。),完全可以把强转这个步骤放在生成器里面,一步到位。

而这种做法会有一层lambda的间接调用(或许编译器能够聪明到理解含义并且优化成一个强转),效率可能会略低一些。

以上是关于将接受子类指针的回调函数统一管理的主要内容,如果未能解决你的问题,请参考以下文章

将 Go 函数作为回调传递给 C

如何通过原始指针将闭包作为参数传递给 C 函数?

jquery的回调对象Callbacks详解

将成员函数指针转换为 FreePascal 中的函数指针

如何使用 C++ lambda 将成员函数指针转换为普通函数指针以用作回调

jquery源码01---(2880 , 3042) Callbacks : 回调对象 : 对函数的统一管理