如何创建可存储任意参数函数的指针以实现引擎事件系统?
问题解答
1. 这种实现是否可行?
完全可行。C++的std::function结合lambda表达式可以轻松实现你的需求,能够适配任意参数列表、任意返回值类型的函数,无需为每个函数单独编写事件设置和执行逻辑。
2. 具体实现方案
根据你是否需要在执行时动态传递参数,提供两种场景的实现:
场景1:创建事件时绑定所有参数,执行时直接调用
适合参数固定的事件,创建时就把函数和参数绑定好,执行时无需额外传参。
#include <functional> #include <tuple> #include <utility> struct sEvent { std::function<void()> _storedFunc; bool _funcStored = false; template <typename Function, typename... Args> bool storeFunc(Function&& func, Args&&... args) { if (_funcStored) return false; // 完美转发捕获函数和参数,绑定为无参可调用对象 _storedFunc = [ func = std::forward<Function>(func), args = std::make_tuple(std::forward<Args>(args)...) ]() { std::apply(func, args); }; _funcStored = true; return true; } void executeFunc() { if (_funcStored) { _storedFunc(); } // 可添加错误处理,比如抛出异常或返回错误码 } };
使用示例:
#include <iostream> void testFunc(int a, float b) { std::cout << "testFunc: " << a << ", " << b << std::endl; } int returnValueFunc() { std::cout << "returnValueFunc called" << std::endl; return 42; } int main() { sEvent event1; event1.storeFunc(testFunc, 10, 3.14f); event1.executeFunc(); // 输出:testFunc: 10, 3.14 sEvent event2; event2.storeFunc(returnValueFunc); event2.executeFunc(); // 输出:returnValueFunc called(返回值被忽略) return 0; }
场景2:事件存储参数列表,执行时动态传递参数
适合参数需要在执行前动态设置的场景,事件存储参数集合,执行时根据函数类型解析参数并传递。
#include <functional> #include <tuple> #include <vector> #include <stdexcept> #include <utility> // 自定义参数存储结构体,支持多类型参数 struct sEventArg { enum class Type { Int32, Float }; Type type; union { int int_val; float float_val; }; sEventArg(int val) : type(Type::Int32), int_val(val) {} sEventArg(float val) : type(Type::Float), float_val(val) {} int get_int32() const { if (type != Type::Int32) throw std::runtime_error("参数类型不匹配"); return int_val; } float get_float() const { if (type != Type::Float) throw std::runtime_error("参数类型不匹配"); return float_val; } }; // 函数特性工具类,用于获取函数的参数和返回值类型 template<typename Func> struct function_traits; template<typename Ret, typename... Args> struct function_traits<Ret(Args...)> { using args_tuple = std::tuple<Args...>; using return_type = Ret; }; template<typename Ret, typename... Args> struct function_traits<std::function<Ret(Args...)>> : function_traits<Ret(Args...)> {}; template<typename Func> struct function_traits : function_traits<decltype(&Func::operator())> {}; template<typename Ret, typename Class, typename... Args> struct function_traits<Ret(Class::*)(Args...) const> : function_traits<Ret(Args...)> {}; // 模板化事件类,适配特定函数类型 template<typename Func> struct sEvent { using ReturnType = typename function_traits<Func>::return_type; using ArgsTuple = typename function_traits<Func>::args_tuple; Func _storedFunc; std::vector<sEventArg> _args; bool _funcStored = false; bool storeFunc(Func&& func) { if (_funcStored) return false; _storedFunc = std::forward<Func>(func); _funcStored = true; return true; } // 设置事件参数 template<typename... Args> void setArgs(Args&&... args) { _args.clear(); (_args.emplace_back(std::forward<Args>(args)), ...); } ReturnType executeFunc() { if (!_funcStored) throw std::runtime_error("未存储函数"); if (_args.size() != std::tuple_size_v<ArgsTuple>) { throw std::runtime_error("参数数量不匹配"); } // 将sEventArg列表转换为函数所需的参数元组 auto arg_tuple = convertArgs(std::index_sequence_for<typename std::tuple_element_t<0, ArgsTuple>, ...>{}); return std::apply(_storedFunc, arg_tuple); } private: // 单参数类型转换 template<typename T> T convertArg(const sEventArg& arg); template<> int convertArg<int>(const sEventArg& arg) { return arg.get_int32(); } template<> float convertArg<float>(const sEventArg& arg) { return arg.get_float(); } // 批量转换参数生成元组 template<size_t... Is> ArgsTuple convertArgs(std::index_sequence<Is...>) { return std::make_tuple(convertArg<typename std::tuple_element_t<Is, ArgsTuple>>(_args[Is])...); } };
使用示例:
#include <iostream> void printValues(int a, float b) { std::cout << "Values: " << a << ", " << b << std::endl; } int sum(int a, int b) { return a + b; } int main() { sEvent<decltype(&printValues)> event1; event1.storeFunc(printValues); event1.setArgs(20, 6.28f); event1.executeFunc(); // 输出:Values: 20, 6.28 sEvent<decltype(&sum)> event2; event2.storeFunc(sum); event2.setArgs(15, 27); int result = event2.executeFunc(); std::cout << "Sum result: " << result << std::endl; // 输出:Sum result: 42 return 0; }
你之前代码的问题分析
- 最初的
storeFunc中,_storedFunc = std::forward<Function>(func)(std::forward<Args>(args)...);是直接执行了函数,把返回值赋值给_storedFunc,这完全错误,应该是绑定函数和参数生成可调用对象,而非立即执行。 - 最后尝试的代码中,lambda用
[=]捕获右值引用参数可能导致拷贝错误,应该用完美转发方式捕获;同时std::function<void()>只能存储返回void的函数,无法适配非void返回值的函数,解决方案是忽略返回值或模板化事件类。
内容的提问来源于stack exchange,提问作者Vandour
相关产品推荐
相关产品推荐

