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

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.dtcms.com/a/75566.html

相关文章:

  • 解决Java多张图合成JPG时出现红色前景及多列自适应适配
  • 《基于Spring Boot+Vue的智慧养老系统的设计与实现》开题报告
  • CAN及CANFD协议
  • 第四届光学与机器视觉国际学术会议(ICOMV 2025)
  • RabbitMQ 和 Redis 的选择
  • ssm框架之mybatis框架搭建
  • 牛客周赛 Round 85(DE)
  • 蓝桥杯备赛(基础语法4)
  • Shell 脚本中的 `read` 命令:灵活处理用户输入
  • 20. Excel 自动化:Excel 对象模型
  • 【NeurIPS-2022】CodeFormer: 将人脸复原转化为码本预测以减少LQ-HQ映射的不确定性
  • 基于ssm学科竞赛小程序的设计及实现(源码+lw+部署文档+讲解),源码可白嫖!
  • 使用Flux查询数据
  • (6)用于无GPS导航的Nooploop
  • [原创](Modern C++)现代C++的关键性概念: 灵活多变的绑定: std::bind
  • 化学工业领域 - 石油化工、高分子化工、生物化工极简理解
  • 软考 中级软件设计师 考点知识点笔记总结 day05
  • E1-数组的平衡点2(前缀和)
  • .[OnlyBuy@cyberfear.com].REVRAC勒索mysql恢复---惜分飞
  • 基于 ELK、Python、OLaMA 和飞书群的 AI 自动化巡检方案
  • 25. K 个一组翻转链表(C++)
  • 微服务面试题:远程调用
  • 注解与设计模式:解锁Java编程的魔法与艺术!
  • mac npm run dev报错 error:0308010C:digital envelope routines::unsupported
  • Redis基础:命令行操作实践指南
  • QML与C++交互
  • Flink读取Kafka数据写入IceBerg(HiveCatalog)
  • 汽车一键启动系统使用方便,舒适出行,轻松匹配
  • Java 中线程废弃方法(stop、suspend、resume)原因及替代方案
  • 2025年春季学期《算法分析与设计》练习4