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

C++14下针对事件类层级验证可变参std::function回调参数合法性

C++14 事件系统注册期参数校验实现方案

核心思路

为每个事件子类绑定专属的回调参数类型定义,优先通过编译期静态校验保证参数匹配,必要时补充运行时校验逻辑,将参数不匹配的错误提前到注册阶段暴露,完全规避触发时静默失败问题。

具体实现步骤

1. 改造事件类层级,绑定参数类型

为每个事件子类增加固定的回调参数类型别名,可选增加运行时参数类型查询接口:

// 增加标准库依赖头文件
#include <type_traits>
#include <typeinfo>
#include <stdexcept>

enum class EventKind {
   first_kind, second_kind, third_kind
};

class EventBase {
public:
    virtual EventKind kind() const = 0;
    virtual bool matches(const EventBase&) const = 0;
    // 可选:运行时参数类型查询接口,用于多态场景校验
    virtual const std::type_info& callback_args_type() const = 0;
    // 可选:克隆接口,用于多态场景下的事件对象拷贝
    virtual EventBase* clone() const = 0;
    virtual ~EventBase() = default;
};

class FirstEvent : public EventBase {
public:
    // 定义该事件对应的回调参数类型,按需修改为实际参数列表
    using CallbackArgs = std::tuple<int, const std::string&>;

    EventKind kind() const final { return EventKind::first_kind; }
    bool matches(const EventBase&) const final;

    const std::type_info& callback_args_type() const final {
        return typeid(CallbackArgs);
    }

    FirstEvent* clone() const final {
        return new FirstEvent(*this);
    }
};

// 其他事件子类按相同规则增加CallbackArgs别名和对应接口实现
class SecondEvent : public EventBase {
public:
    using CallbackArgs = std::tuple<uint32_t, double>;
    EventKind kind() const final { return EventKind::second_kind; }
    bool matches(const EventBase&) const final;
    const std::type_info& callback_args_type() const final {
        return typeid(CallbackArgs);
    }
    SecondEvent* clone() const final {
        return new SecondEvent(*this);
    }
};

2. 实现编译期静态校验的注册接口

修改EventManager的add_listener为模板接口,直接接收具体事件子类实例,通过std::enable_if约束参数匹配,不匹配的代码会直接编译报错:

class EventManager {
public:
    // 优先使用的接口:接收具体事件子类,编译期校验参数匹配
    template<typename EventT, typename... Args, 
             typename = typename std::enable_if<
                 std::is_base_of<EventBase, EventT>::value &&
                 std::is_same<std::tuple<Args...>, typename EventT::CallbackArgs>::value
             >::type>
    void add_listener(const EventT& event, std::function<void(Args...)> listener) {
        auto sub = new Sub<Args...>();
        sub->event = new EventT(event);
        sub->listener = std::move(listener);
        subscriptions[event.kind()].push_back(sub);
    }

    // 可选:兼容接收EventBase基类的多态场景,注册期运行时校验
    template<typename... Args>
    void add_listener(const EventBase& event, std::function<void(Args...)> listener) {
        // 注册时直接校验参数类型,不匹配立即抛出异常/触发断言
        if (typeid(std::tuple<Args...>) != event.callback_args_type()) {
            throw std::invalid_argument("Listener argument type mismatch with event");
        }
        auto sub = new Sub<Args...>();
        sub->event = event.clone();
        sub->listener = std::move(listener);
        subscriptions[event.kind()].push_back(sub);
    }
    
    // 可选:给trigger也增加参数校验逻辑,避免触发时参数错误
    template<typename EventT, typename... Args,
             typename = typename std::enable_if<
                 std::is_base_of<EventBase, EventT>::value &&
                 std::is_same<std::tuple<Args...>, typename EventT::CallbackArgs>::value
             >::type>
    void trigger(const EventT& event, Args... args) {
        for (auto s : subscriptions[event.kind()]) {
            if (event.matches(*(s->event))) {
                auto* ss = dynamic_cast<Sub<Args...> *>(s);
                if (ss && ss->listener) { ss->listener(args...); }
            }
        }
    }

    ~EventManager() {
        // 补充析构逻辑避免内存泄漏
        for (auto& pair : subscriptions) {
            for (auto sub : pair.second) {
                delete sub;
            }
        }
    }

private:
    struct SubBase {
        EventBase* event;
        virtual ~SubBase() { delete event; }
    };
    template<typename... Args>
    struct Sub : public SubBase {
        std::function<void(Args...)> listener;
    };

    std::map<EventKind, std::vector<SubBase *>> subscriptions;
};

兼容说明

所有实现仅使用C++14标准特性,完全兼容Visual Studio 2015、GCC 4.9.2运行环境。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 15:45:02