0 摘要
以常用的某品牌相机的驱动为例,分析回调机制的实现。
1 SetCallback(即,注册回调)的实现
1.1 函数签名
- int MV_CC_RegisterImageCallBackEx(
- void* handle, // 相机设备句柄
- void (__stdcall *pCallBack)(unsigned char * pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser), // 回调函数指针
- void* pUser // 用户自定义数据
- );
复制代码 参数说明:
- handle:相机设备句柄,用于标识具体的相机设备
- pCallBack:回调函数指针,使用__stdcall调用约定
- pUser:用户数据(即,上下文指针),会在回调时原样传回
1.2 内部实现
- // 内部数据结构
- struct CameraDevice {
- void* hardwareHandle; // 硬件设备句柄
- void* userCallback; // 用户回调函数指针
- void* userData; // 用户数据
- std::thread* captureThread; // 采集线程
- bool isGrabbing; // 采集状态
- // ... 其他成员
- };
- // 函数实现
- int MV_CC_RegisterImageCallBackEx(void* handle,
- void (__stdcall *pCallBack)(unsigned char *, MV_FRAME_OUT_INFO_EX*, void*),
- void* pUser) {
- // 1. 参数验证
- if (!handle || !pCallBack) {
- return MV_E_PARAMETER; // 参数错误
- }
-
- // 2. 获取设备对象
- CameraDevice* device = static_cast<CameraDevice*>(handle);
-
- // 3. 保存回调函数和用户数据
- device->userCallback = reinterpret_cast<void*>(pCallBack);
- device->userData = pUser;
-
- // 4. 返回成功
- return MV_OK;
- }
复制代码 2 回调触发机制
- // 采集线程
- void CaptureThreadProc(CameraDevice* device) {
- while (device->isGrabbing) {
- // 1. 从硬件获取图像数据
- unsigned char* imageData = nullptr;
- MV_FRAME_OUT_INFO_EX frameInfo = {0};
-
- int result = GetImageFromHardware(device->hardwareHandle, &imageData, &frameInfo);
-
- if (result == MV_OK && imageData != nullptr) {
- // 2. 检查是否有注册的回调函数
- if (device->userCallback) {
- // 3. 调用用户回调函数
- auto callback = reinterpret_cast<void (__stdcall *)(unsigned char *, MV_FRAME_OUT_INFO_EX*, void*)>
- (device->userCallback);
-
- // 4. 在单独的线程或当前线程中执行回调
- callback(imageData, &frameInfo, device->userData);
- }
- }
-
- // 5. 短暂休眠,避免过度占用CPU
- std::this_thread::sleep_for(std::chrono::microseconds(100));
- }
- }
- int MV_CC_StartGrabbing(void* handle) {
- CameraDevice* device = static_cast<CameraDevice*>(handle);
-
- if (device->isGrabbing) {
- return MV_E_CALLORDER; // 已经在采集中
- }
-
- // 启动采集线程
- device->isGrabbing = true;
- device->captureThread = new std::thread(CaptureThreadProc, device);
-
- return MV_OK;
- }
复制代码 3. 关键设计要点
- 函数指针存储:SDK内部保存用户提供的回调函数指针
- 用户数据传递:将用户数据原样保存,回调时传回
- 线程安全:在采集线程中调用回调,需要考虑线程安全
- 异常处理:回调执行异常不应影响采集线程的正常运行
4 实际应用中的考虑
4.1 性能考虑
- // 回调应该快速执行,避免阻塞采集线程
- void ImageCB(...) {
- // 快速处理或提交到线程池
- // 避免在回调中执行耗时操作
- }
复制代码 4.2 错误处理
- // SDK应该处理回调中的异常
- void CaptureThreadProc(CameraDevice* device) {
- try {
- if (device->userCallback) {
- callback(imageData, &frameInfo, device->userData);
- }
- } catch (...) {
- // 记录错误,但不影响采集线程
- LogError("Callback execution failed");
- }
- }
复制代码 4.3 资源管理
- // 确保回调中正确管理资源
- void ImageCB(...) {
- // 使用智能指针或RAII管理临时资源
- std::unique_ptr<unsigned char[]> buffer(pData);
- // ... 处理逻辑 ...
- // 自动释放内存
- }
复制代码 5 最佳实践的建议
- class Camera {
- private:
- // 1. 静态桥接函数
- static void __stdcall CallbackBridge(/* 参数 */, void* userData) {
- // 参数验证
- if (!userData) return;
-
- // 恢复对象指针
- Camera* self = static_cast<Camera*>(userData);
-
- // 异常处理
- try {
- self->HandleCallback(/* 参数 */);
- } catch (...) {
- // 错误处理
- }
- }
-
- // 2. 实际处理函数(非静态)
- void HandleCallback(/* 参数 */) {
- // 直接访问成员变量
- // 实现业务逻辑
- }
-
- // 3. 注册回调
- void RegisterCallback() {
- SDK_RegisterCallback(CallbackBridge, this);
- }
- };
复制代码 这种设计既保持了与C风格API的兼容性,又能在实际处理函数(即,HandleCallback)中,获得面向对象编程的便利性。
6 相关设计模式
6.1 观察者模式(Observer Pattern)
回调机制本质上是观察者模式的简化版本:
- Subject:相机SDK
- Observer:用户回调函数
- 通知机制:函数指针调用
6.2 策略模式(Strategy Pattern)
回调函数可以看作是可替换的算法策略:
- Context:相机采集过程
- Strategy:用户提供的处理函数
- 执行时机:图像采集完成时
6.3 模板方法模式(Template Method)
SDK定义了采集的算法骨架,用户定义具体的处理步骤:
7 总结
回调机制:
- 注册机制:SDK保存用户提供的函数指针和上下文数据
- 触发机制:在特定事件发生时调用保存的函数指针
- 数据传递:通过参数将事件相关数据传递给回调函数
- 上下文恢复:通过用户数据参数恢复调用上下文
回调机制在设备驱动、GUI框架、网络库,等领域广泛应用,是实现松耦合、事件驱动的重要工具。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |