Modern C++处理 Hooks 机制
在 Modern C++(C++11 及更高标准) 中,处理 Hooks 机制有更灵活且类型安全的方式,主要借助于 std::function
、lambda 表达式、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::optional
和 CRTP 模式确保类型安全。
📌 示例实现
#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 表达式等),并且对 性能 和 类型安全 也更有优势。
✅ 为什么选择可变参数模板实现多播委托?
- 支持多种回调类型(Lambda、成员函数、普通函数、函数对象)。
- 类型安全,不会因
std::function
的类型擦除而丢失具体类型。 - 性能更好,避免
std::function
的动态分配和类型擦除,模板可内联。 - 扩展性强,可以轻松添加异步触发、回调优先级、绑定上下文等高级功能。
📌 基于 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
实现的多播委托可以灵活应对多种复杂场景,同时保持高性能和类型安全。