C++ 回调函数的几种策略
Stackoverflow中提出了这样一个问题:假设我们实现了一个User类,Library类,现在Library类中utility需要回调User中func方法,总结答案,常见的几种方法如下:
- 静态方法:将User类中func方法实现为静态方法,Library类中进行调用。
- 虚拟方法:将User类继承至基类Base,Base类中func方法为纯虚函数。
- 直接回调:在Library中保存User类指针,utility方法中直接调用。
下面则是非常规方法。
基于tag dispatch的调用方法,利用空类实现标签回调,在注册方法时提供不同的空类模板参数实现不同的调用,代码如下:
template<class T> struct tag_t { using type = T; constexpr tag_t() {} }; struct ForLibrary; template <class T> class Library { public: T *node = nullptr; Library(T *n):node(n) {} void utility() { func(tag_t<ForLibrary>(),node); }; }; class User { public: void func() { cout << "User::func" << endl; } friend void func(tag_t<ForLibrary>, User *self) { self->func(); } };
不得不说,这种方法太妙了,既可以动态变化回调方法(只需修改ForLibrary参数)又无需改变User类实现,而且一切都是静态调用无虚函数开销。
利用模板基类实现静态多态机制,通过在基类中强转类型调用子类方法,实现路由回调,代码如下:
#include <iostream> #include <functional> template<typename T> class ICallback { public: void callback() { static_cast<T*>(this)->func(); } }; class User : public ICallback<User> { public: void func(){ std::cout << "User::func" << std::endl; } }; template<class T,class F=std::function<void(void)>> class Library { private: T *node; public: Library(T* n):node(n) { } void utility() { std::mem_fn(&T::callback)(*node); } void utility(F func) { func(); } }; int main() { User user; Library<User> lib(&user); lib.utility(); lib.utility(std::bind(&User::callback,user)); return 0; }
回调类与实现类相互解耦又能避免虚函数多态。