当前位置: 首页 > article >正文

Modern C++处理 Hooks 机制

Modern C++(C++11 及更高标准) 中,处理 Hooks 机制有更灵活且类型安全的方式,主要借助于 std::functionlambda 表达式std::map 以及 CRTP 等特性,避免了旧标准中依赖的 函数指针C-style 结构体。下面我们来详细探讨几种更现代的实现方式。

1. 使用 std::function + Lambda

std::function 是 C++11 引入的通用函数包装器,支持存储:

  • 普通函数
  • 成员函数
  • lambda 表达式
  • 仿函数

📌 实现示例

#include <iostream>
#include <functional>
#include <unordered_map>
#include <string>

// 定义 Hook 类型
using Hook = std::function<void()>;

// Hook 管理类
class HookManager {
public:
    void registerHook(const std::string& name, Hook hook) {
        hooks[name] = std::move(hook);
    }

    void executeHook(const std::string& name) {
        if (hooks.count(name)) {
            hooks[name]();
        } else {
            std::cerr << "Hook not found: " << name << std::endl;
        }
    }

private:
    std::unordered_map<std::string, Hook> hooks;
};

int main() {
    HookManager manager;

    // 注册 Lambda Hook
    manager.registerHook("onStart", []() {
        std::cout << "Application Started!" << std::endl;
    });

    // 注册成员函数 Hook
    struct MyClass {
        void onEvent() { std::cout << "Event Triggered!" << std::endl; }
    } obj;
    manager.registerHook("onEvent", [&]() { obj.onEvent(); });

    // 执行 Hook
    manager.executeHook("onStart");
    manager.executeHook("onEvent");

    return 0;
}

📊 优点

  • 支持多种回调(普通函数、lambda、成员函数)。
  • 代码简洁,增强了类型安全性。
  • 适合需要动态注册和注销 Hook 的场景。

2. 使用 std::vector + 多播委托(多回调支持)

如果需要在一个 Hook 点执行多个回调,可以使用 std::vector 存储多个 std::function

📌 实现示例

#include <iostream>
#include <vector>
#include <functional>

class Event {
public:
    void registerHook(const std::function<void()>& hook) {
        hooks.push_back(hook);
    }

    void trigger() {
        for (const auto& hook : hooks) {
            hook();
        }
    }

private:
    std::vector<std::function<void()>> hooks;
};

int main() {
    Event onShutdown;

    onShutdown.registerHook([] { std::cout << "Saving data..." << std::endl; });
    onShutdown.registerHook([] { std::cout << "Releasing resources..." << std::endl; });

    onShutdown.trigger();

    return 0;
}

📊 优点

  • 支持 多回调,适用于事件广播(类似于 C# 的 MulticastDelegate)。
  • Hook 顺序执行,轻松添加和删除回调。

 

3. 使用模板 + CRTP 实现类型安全的 Hook

Curiously Recurring Template Pattern (CRTP) 结合 std::function,可以实现更强的类型检查和编译期优化。

📌 实现示例

#include <iostream>
#include <functional>

template <typename Derived>
class Hookable {
public:
    using HookType = std::function<void(Derived&)>;

    void setHook(HookType hook) {
        hook_ = std::move(hook);
    }

    void runHook() {
        if (hook_) hook_(static_cast<Derived&>(*this));
    }

private:
    HookType hook_;
};

class MyComponent : public Hookable<MyComponent> {
public:
    void doWork() {
        std::cout << "Doing work..." << std::endl;
        runHook();
    }
};

int main() {
    MyComponent component;

    // 注册 Hook,类型安全
    component.setHook([](MyComponent& c) {
        std::cout << "Post-processing after work." << std::endl;
    });

    component.doWork();

    return 0;
}

📊 优点

  • 静态类型安全,防止传入错误的回调函数。
  • 不需要虚函数,运行时性能开销更小。

4. 使用 Observer 模式

适用于复杂的 Hook 需求,多个观察者响应特定事件。

📌 实现示例

#include <iostream>
#include <vector>
#include <functional>

class Subject {
public:
    using Observer = std::function<void(int)>;

    void addObserver(const Observer& obs) {
        observers.push_back(obs);
    }

    void notify(int value) {
        for (auto& obs : observers) {
            obs(value);
        }
    }

private:
    std::vector<Observer> observers;
};

class Logger {
public:
    void log(int value) { std::cout << "Logging: " << value << std::endl; }
};

int main() {
    Subject subject;
    Logger logger;

    subject.addObserver([](int value) {
        std::cout << "Received value: " << value << std::endl;
    });

    subject.addObserver([&](int value) {
        logger.log(value);
    });

    subject.notify(42);

    return 0;
}

📊 优点

  • 解耦:观察者与被观察者独立,符合 开闭原则
  • 多播机制:适合多方监听同一事件。

5. 使用 std::optional 支持可选 Hook

某些 Hook 可能是 可选 的,可以用 std::optional 来简化这种场景。

📌 实现示例

#include <iostream>
#include <optional>
#include <functional>

class Task {
public:
    using Hook = std::function<void()>;

    void setHook(Hook hook) { hook_ = std::move(hook); }

    void run() {
        std::cout << "Running task..." << std::endl;
        if (hook_) hook_.value()();
    }

private:
    std::optional<Hook> hook_;
};

int main() {
    Task task;

    task.setHook([] { std::cout << "Post-task cleanup." << std::endl; });

    task.run();

    return 0;
}

 

📊 优点

  • 简洁:无需额外的条件检查。
  • 安全:只在 Hook 存在时执行。

📌 总结

方案适用场景特点
std::function + lambda通用 Hook,动态回调简单、灵活,支持多种回调类型
多播委托 (std::vector)多个回调同时执行支持多回调,按顺序执行
CRTP 模板类型安全的 Hook,编译时确定高性能,适合特定场景
观察者模式 (Observer)复杂事件通知,多个组件对同一事件作出响应事件驱动、解耦
std::optional + Hook可选回调适合有条件执行的 Hook

根据实际需求选择最适合的 Hook 管理方案,Modern C++ 提供了多种更高效、安全、灵活的方法取代传统的函数指针和结构体设计。

 注:

旧Hook机制指的是类似下面这种代码:

CDK_VISIBILITY_PUBLIC VOID ChiNodeEntry(
    CHINODECALLBACKS* pNodeCallbacks)
{
    if (NULL != pNodeCallbacks)
    {
        if (pNodeCallbacks->majorVersion == ChiNodeMajorVersion &&
            pNodeCallbacks->size >= sizeof(CHINODECALLBACKS))
        {
            pNodeCallbacks->majorVersion              = ChiNodeMajorVersion;
            pNodeCallbacks->minorVersion              = ChiNodeMinorVersion;
            pNodeCallbacks->pGetCapabilities          = EISV2NodeGetCaps;
            pNodeCallbacks->pQueryVendorTag           = EISV2NodeQueryVendorTag;
            pNodeCallbacks->pCreate                   = EISV2NodeCreate;
            pNodeCallbacks->pDestroy                  = EISV2NodeDestroy;
            pNodeCallbacks->pQueryBufferInfo          = EISV2NodeQueryBufferInfo;
            pNodeCallbacks->pSetBufferInfo            = EISV2NodeSetBufferInfo;
            pNodeCallbacks->pProcessRequest           = EISV2NodeProcRequest;
            pNodeCallbacks->pChiNodeSetNodeInterface  = EISV2NodeSetNodeInterface;
            pNodeCallbacks->pPostPipelineCreate       = EISV2NodePostPipelineCreate;
            pNodeCallbacks->pQueryMetadataPublishList = EISV2NodeQueryMetadataPublishList;
...

📊 为什么说这种方式“过时”

虽然这种方式在 C 中很常见,但在 Modern C++ 中已经有更现代、更安全的实现方式,原因包括:

  • 函数指针缺乏类型安全,容易引发未定义行为。
  • 无法捕获上下文,对 C++ 类的成员函数支持不友好。
  • 不方便管理生命周期,很难追踪 Hook 的生存期。

如何用 Modern C++ 改进这种 Hooks 机制

1️⃣ 使用 std::function 替代裸函数指针

std::function类型安全支持捕获上下文可以绑定成员函数,更适用于现代 C++。

#include <iostream>
#include <functional>

// 替代 CHINODECALLBACKS 结构
struct NodeCallbacks {
    std::function<void()> onCreate;
    std::function<void()> onDestroy;
    std::function<void(int requestId)> onProcessRequest;
};

// 节点实现
class MyNode {
public:
    void registerCallbacks(NodeCallbacks& callbacks) {
        callbacks.onCreate = [this]() { this->onCreate(); };
        callbacks.onDestroy = [this]() { this->onDestroy(); };
        callbacks.onProcessRequest = [this](int id) { this->processRequest(id); };
    }

    void onCreate() { std::cout << "Node Created\n"; }
    void onDestroy() { std::cout << "Node Destroyed\n"; }
    void processRequest(int requestId) {
        std::cout << "Processing request " << requestId << std::endl;
    }
};

// 框架调用示例
int main() {
    NodeCallbacks callbacks;
    MyNode node;

    node.registerCallbacks(callbacks);

    callbacks.onCreate();
    callbacks.onProcessRequest(42);
    callbacks.onDestroy();

    return 0;
}

优点

  • 更强的类型安全性,避免了函数指针的类型不匹配问题。
  • 支持类成员函数,可以使用 lambda 捕获对象上下文。
  • 生命周期管理更简单,可以使用智能指针捕获资源。

2️⃣ 使用 std::map + 多播回调

如果需要多个组件响应一个事件,使用 std::map 存储回调,类似于多播代理。

📌 示例实现
#include <iostream>
#include <unordered_map>
#include <functional>
#include <string>

class HookManager {
public:
    using Hook = std::function<void(int)>;

    void registerHook(const std::string& name, Hook hook) {
        hooks[name] = std::move(hook);
    }

    void invokeHook(const std::string& name, int requestId) {
        if (hooks.count(name)) {
            hooks[name](requestId);
        } else {
            std::cout << "Hook not found: " << name << std::endl;
        }
    }

private:
    std::unordered_map<std::string, Hook> hooks;
};

int main() {
    HookManager manager;

    manager.registerHook("ProcessRequest", [](int id) {
        std::cout << "Processing Request ID: " << id << std::endl;
    });

    manager.invokeHook("ProcessRequest", 1001);

    return 0;
}

适用场景

  • 支持动态注册和注销 Hook。
  • 适合有多种不同回调且需要按需触发的场景。

3️⃣ 结合 CRTP + 可选 Hook

如果 Hook 是可选的,可以使用 std::optionalCRTP 模式确保类型安全。

📌 示例实现
#include <iostream>
#include <optional>
#include <functional>

template <typename Derived>
class Hookable {
public:
    using Hook = std::function<void(Derived&, int)>;

    void setHook(Hook hook) { mHook = std::move(hook); }

    void executeHook(int value) {
        if (mHook) mHook.value()(static_cast<Derived&>(*this), value);
    }

private:
    std::optional<Hook> mHook;
};

class MyNode : public Hookable<MyNode> {
public:
    void process(int id) {
        std::cout << "Processing ID: " << id << std::endl;
        executeHook(id);
    }
};

int main() {
    MyNode node;

    // 注册 Hook
    node.setHook([](MyNode&, int id) {
        std::cout << "Hook Triggered for ID: " << id << std::endl;
    });

    node.process(2001);
    return 0;
}

优点

  • 类型安全,避免函数指针类型错误。
  • 更灵活的拓展性,适用于复杂的 Camera HAL 场景。

🎯 总结:Modern C++ 优化 Hooks 机制

方法特点适用场景
函数指针 + 结构体传统方法,兼容 C,效率高但类型不安全适用于 C-style 框架,如 Camera HAL
std::function + Lambda类型安全,支持成员函数和 lambda适合需要动态注册和复杂上下文捕获的 Hook
std::map + 多播回调多回调管理,支持动态注册、注销适合多个组件监听一个事件,如多通道图像处理
CRTP + std::optional静态类型检查,编译时多态,回调可选高性能、生命周期严格管理的 Camera Pipeline
Observer 模式支持多观察者,松耦合,扩展性强适合复杂依赖关系的图像处理管道

👉 推荐

  • 简单 Hooks:使用 std::function + Lambda。
  • 复杂 Hooks:使用 CRTP、Observer 模式实现多级回调与类型安全。

使用 std::vector + 多播委托,我觉得使用可变参数模板比你的std::function更灵活(当然也更难写)

确实,使用 可变参数模板(variadic templates) 可以实现更灵活和通用的多播委托(Multicast Delegate)。与 std::function 相比,使用模板不仅能够更精确地控制函数签名,还能支持多种回调形式(例如类成员函数、静态函数、Lambda 表达式等),并且对 性能类型安全 也更有优势。


为什么选择可变参数模板实现多播委托?

  1. 支持多种回调类型(Lambda、成员函数、普通函数、函数对象)。
  2. 类型安全,不会因 std::function 的类型擦除而丢失具体类型。
  3. 性能更好,避免 std::function 的动态分配和类型擦除,模板可内联。
  4. 扩展性强,可以轻松添加异步触发、回调优先级、绑定上下文等高级功能。

📌 基于 std::vector + 可变参数模板实现多播委托

1️⃣ 基础实现

支持普通函数、Lambda、成员函数回调的多播委托。

#include <iostream>
#include <vector>
#include <functional>
#include <utility>

// 多播委托模板
template <typename... Args>
class MulticastDelegate {
public:
    // 注册回调(普通函数、Lambda、函数对象)
    template <typename Callable>
    void add(Callable&& func) {
        callbacks.emplace_back(std::forward<Callable>(func));
    }

    // 注册类成员函数
    template <typename T>
    void add(T* instance, void (T::*method)(Args...)) {
        callbacks.emplace_back([instance, method](Args... args) {
            (instance->*method)(std::forward<Args>(args)...);
        });
    }

    // 执行所有回调
    void invoke(Args... args) const {
        for (const auto& callback : callbacks) {
            callback(args...);
        }
    }

    // 清除所有回调
    void clear() {
        callbacks.clear();
    }

private:
    std::vector<std::function<void(Args...)>> callbacks;
};

// 示例使用
class MyNode {
public:
    void onEvent(int id, const std::string& msg) {
        std::cout << "[MyNode] Event: " << id << " - " << msg << std::endl;
    }
};

void globalHandler(int id, const std::string& msg) {
    std::cout << "[Global] Event: " << id << " - " << msg << std::endl;
}

int main() {
    MulticastDelegate<int, std::string> delegate;
    MyNode node;

    // 注册普通函数
    delegate.add(globalHandler);

    // 注册类成员函数
    delegate.add(&node, &MyNode::onEvent);

    // 注册 Lambda
    delegate.add([](int id, const std::string& msg) {
        std::cout << "[Lambda] Event: " << id << " - " << msg << std::endl;
    });

    // 触发回调
    delegate.invoke(42, "Hello World");

    return 0;
}

输出结果

[Global] Event: 42 - Hello World
[MyNode] Event: 42 - Hello World
[Lambda] Event: 42 - Hello World

🔥 2️⃣ 高级版:支持移除回调、异步执行、返回值聚合

支持移除回调

为每个回调生成唯一 ID,方便移除特定回调。

#include <iostream>
#include <vector>
#include <functional>
#include <utility>
#include <algorithm>
#include <atomic>

template <typename... Args>
class AdvancedMulticastDelegate {
public:
    using Callback = std::function<void(Args...)>;
    using CallbackId = std::size_t;

    // 添加回调并返回唯一 ID
    CallbackId add(Callback&& callback) {
        auto id = nextId++;
        callbacks.emplace_back(id, std::move(callback));
        return id;
    }

    // 删除回调
    void remove(CallbackId id) {
        callbacks.erase(std::remove_if(callbacks.begin(), callbacks.end(),
                                       [id](const auto& pair) { return pair.first == id; }),
                        callbacks.end());
    }

    // 执行所有回调
    void invoke(Args... args) const {
        for (const auto& [id, callback] : callbacks) {
            callback(args...);
        }
    }

private:
    std::vector<std::pair<CallbackId, Callback>> callbacks;
    std::atomic<CallbackId> nextId{0};
};

// 示例
void testHandler(int x) {
    std::cout << "Handler called with " << x << std::endl;
}

int main() {
    AdvancedMulticastDelegate<int> delegate;

    // 添加回调
    auto id1 = delegate.add(testHandler);
    auto id2 = delegate.add([](int x) { std::cout << "Lambda: " << x << std::endl; });

    // 执行
    delegate.invoke(123);

    // 删除第一个回调
    delegate.remove(id1);
    std::cout << "After removal:" << std::endl;
    delegate.invoke(456);

    return 0;
}

🧰 3️⃣ 返回值聚合

如果需要让多播委托的回调返回值并聚合结果,使用 std::vector 收集回调返回值。

#include <iostream>
#include <vector>
#include <functional>
#include <utility>

template <typename ReturnType, typename... Args>
class CollectingDelegate {
public:
    using Callback = std::function<ReturnType(Args...)>;

    void add(Callback&& callback) {
        callbacks.emplace_back(std::move(callback));
    }

    std::vector<ReturnType> invoke(Args... args) const {
        std::vector<ReturnType> results;
        for (const auto& callback : callbacks) {
            results.push_back(callback(args...));
        }
        return results;
    }

private:
    std::vector<Callback> callbacks;
};

// 示例
int doubleValue(int x) { return x * 2; }

int main() {
    CollectingDelegate<int, int> delegate;
    delegate.add(doubleValue);
    delegate.add([](int x) { return x + 1; });

    auto results = delegate.invoke(10);

    for (const auto& result : results) {
        std::cout << "Result: " << result << std::endl;
    }
    return 0;
}

适用于

  • 需要收集多播结果的场景,例如多传感器数据、图像处理等。
  • 复杂场景下的多线程执行与返回值收集。

🎯 总结

实现方式特点适用场景
基础版 (std::function)支持普通函数、Lambda、成员函数,简单灵活普通多播委托,回调不需要动态管理
高级版(带回调 ID 和移除功能)支持回调移除、管理生命周期,扩展性强多任务处理、动态注册与注销
返回值聚合版聚合回调返回值,适合需要数据整合的场景图像处理、数据分析、多传感器输入

Modern C++ 中,使用 可变参数模板 + std::vector 实现的多播委托可以灵活应对多种复杂场景,同时保持高性能和类型安全。


http://www.kler.cn/a/593673.html

相关文章:

  • Datawhale大语言模型-Transformer以及模型详细配置
  • HttpClient通讯时间过久
  • MiniMax GenAI 可观测性分析:基于阿里云 SelectDB 构建 PB 级别日志系统
  • python采集小红书笔记详情API接口,json数据示例分享
  • 理工超市-多用户注册
  • 【C++模板】
  • Linux--进程创建
  • Android 13深度定制:SystemUI状态栏时间居中显示终极实战指南
  • The Illustrated Stable Diffusion
  • 电机控制常见面试问题(十四)
  • pytorch v1.4.0安装问题
  • 2024年国赛高教杯数学建模E题交通流量管控解题全过程文档及程序
  • 嵌入式系统开发如何选择和备考软考高级
  • webpack等构建工具如何支持移除未使用的代码
  • 基于carla的模仿学习(附数据集CORL2017)更新中........
  • WPF 中的 GridSplitter 详解
  • 不使用负压电源,ADC如何测量正负压?
  • 为什么渲染农场渲染帧而非视频?核心原因 + 举例
  • Neo4j GDS-02-graph-data-science 简单聊一聊图数据科学插件库
  • 计算机网络基础:设计高效的网络布局