博客 / 詳情

返回

“靜態回調+上下文指針”模式實現回調機制

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框架、網絡庫,等領域廣泛應用,是實現鬆耦合、事件驅動的重要工具。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.