如何实现可适配任意类型函数、API优雅的事件处理机制?
实现自动适配任意函数类型的EventHandler类
可以利用C++的类型擦除、std::function和模板特性,实现一个能自动适配普通函数、类成员函数(含const成员函数)、lambda等各类可调用对象的EventHandler类,无需手动调用std::bind。
核心实现代码
#include <functional> #include <iostream> #include <memory> #include <type_traits> class EventHandler { private: // 类型擦除基类,统一存储不同类型的可调用对象 class BaseCallable { public: virtual ~BaseCallable() = default; }; // 模板子类,封装具体的可调用对象 template<typename Func> class Callable : public BaseCallable { public: // 支持右值和左值构造 Callable(Func&& func) : func_(std::forward<Func>(func)) {} Callable(const Func& func) : func_(func) {} // 完美转发参数调用函数 template<typename... Args> auto operator()(Args&&... args) -> decltype(func_(std::forward<Args>(args)...)) { return func_(std::forward<Args>(args)...); } Func func_; }; std::unique_ptr<BaseCallable> callable_; // 辅助函数:创建对应Callable实例并存储 template<typename Func> void set_callable(Func&& func) { using DecayedFunc = std::decay_t<Func>; callable_ = std::make_unique<Callable<DecayedFunc>>(std::forward<Func>(func)); } public: // 构造函数1:处理普通函数指针 template<typename Ret, typename... Args> EventHandler(Ret(*func)(Args...)) { set_callable(std::function<Ret(Args...)>(func)); } // 构造函数2:处理非const类成员函数,绑定实例 template<typename Ret, typename Class, typename... Args> EventHandler(Ret(Class::*func)(Args...), Class& instance) { // 用占位符适配任意数量参数(这里写了6个,可按需扩展) set_callable(std::bind(func, &instance, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); } // 构造函数3:处理const类成员函数,绑定const实例 template<typename Ret, typename Class, typename... Args> EventHandler(Ret(Class::*func)(Args...) const, const Class& instance) { set_callable(std::bind(func, &instance, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6)); } // 构造函数4:通用构造,支持lambda、std::function等可调用对象 template<typename Func, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Func>, EventHandler>>> EventHandler(Func&& func) { set_callable(std::forward<Func>(func)); } // 模板调用运算符,完美转发参数 template<typename... Args> auto operator()(Args&&... args) -> decltype(auto) { using TargetFunc = std::function<decltype(std::declval<BaseCallable&>().operator()(std::forward<Args>(args)...))(Args...)>; return static_cast<Callable<TargetFunc>*>(callable_.get())->operator()(std::forward<Args>(args)...); } };
使用示例
class Abc { public: void aFunc() { std::cout << "a()" << std::endl; } void cFunc(int x, char y) { std::cout << "c(" << x << ", " << y << ")" << std::endl; } void constFunc() const { std::cout << "constFunc()" << std::endl; } }; void bFunc(int x) { std::cout << "b(" << x << ")" << std::endl; } int main() { Abc abc; const Abc constAbc; // 绑定类非const成员函数 EventHandler a = {&Abc::aFunc, abc}; // 绑定普通函数 EventHandler b = bFunc; // 绑定带参数的类成员函数 EventHandler c = {&Abc::cFunc, abc}; // 绑定const类成员函数 EventHandler d = {&Abc::constFunc, constAbc}; // 绑定lambda表达式 EventHandler e = [](std::string msg) { std::cout << "lambda: " << msg << std::endl; }; // 调用,参数自动匹配 a(); b(123); c(456789, 'f'); d(); e("hello world"); std::cout << "Done." << std::endl; return 0; }
关键说明
- 类型擦除:通过
BaseCallable基类和模板Callable子类,实现对不同类型可调用对象的统一存储,隐藏具体函数类型细节。 - 自动绑定:针对成员函数的构造函数会自动调用
std::bind绑定对象实例,无需用户手动操作。 - 编译期检查:调用
EventHandler时,若参数数量/类型与存储的函数不匹配,编译器会直接报错,保证类型安全。 - 扩展性:如果需要支持更多参数的函数,只需扩展
std::bind中的占位符数量(比如增加到_10)即可。
内容的提问来源于stack exchange,提问作者no one special
相关产品推荐
相关产品推荐

