You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建可存储任意参数函数的指针以实现引擎事件系统?

问题解答

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;
}

你之前代码的问题分析

  1. 最初的storeFunc中,_storedFunc = std::forward<Function>(func)(std::forward<Args>(args)...);是直接执行了函数,把返回值赋值给_storedFunc,这完全错误,应该是绑定函数和参数生成可调用对象,而非立即执行。
  2. 最后尝试的代码中,lambda用[=]捕获右值引用参数可能导致拷贝错误,应该用完美转发方式捕获;同时std::function<void()>只能存储返回void的函数,无法适配非void返回值的函数,解决方案是忽略返回值或模板化事件类。

内容的提问来源于stack exchange,提问作者Vandour

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 12:02:12