“静态回调+上下文指针”模式实现回调机制

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. 关键设计要点

  1. 函数指针存储:SDK内部保存用户提供的回调函数指针

  2. 用户数据传递:将用户数据原样保存,回调时传回

  3. 线程安全:在采集线程中调用回调,需要考虑线程安全

  4. 异常处理:回调执行异常不应影响采集线程的正常运行

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 总结

回调机制:

  1. 注册机制:SDK保存用户提供的函数指针和上下文数据

  2. 触发机制:在特定事件发生时调用保存的函数指针

  3. 数据传递:通过参数将事件相关数据传递给回调函数

  4. 上下文恢复:通过用户数据参数恢复调用上下文

回调机制在设备驱动、GUI框架、网络库,等领域广泛应用,是实现松耦合、事件驱动的重要工具。

posted @ 2025-12-17 22:15  _bob  阅读(6)  评论(0)    收藏  举报