回調函數——一個化解C++互相包含頭文件問題的方法

最近寫了兩個通訊協議程序,都是電力系統中問答式傳輸規約,一個是基於TCP傳輸文件的102規約服務端程序,一個是基於串口採集數據的102規約客戶端程序。之前還有別的通訊規約,最近更濃烈的期望能夠抽象出這些通訊協議程序的一些通用的部分,以便於新的協議來時,我可以少一些工作量。大致上,我將這些通訊程序分成以下幾類:

1、通訊協議類,主要負責按照一定傳輸協議去組裝報文,拆解報文,範圍包括全部角色的報文(主站、子站)
2通訊控制類,包括各種通訊控制的子類:TCP、串口等,主要負責建立通訊、關閉通訊、發送報文、接收報文
3、服務端邏輯類,主要是站在服務端的角色處理邏輯,具有12兩種類型的成員變量
4、客戶端邏輯類,主要是站在客戶端的角色處理邏輯,具有12兩種類型的成員變量

5界面類,負責與用戶、邏輯類交互

聽起來是很美,做到邏輯類的時候也還算順暢,到了做界面類與邏輯類的交互時就糾結啦~我是有強迫症的,如果結構太累贅就心塞~之前寫別的協議時,因爲剛接觸也不太瞭解業務,時間又緊,只好忍着把邏輯類和界面類揉在一起,這次說什麼也要把他們分開!不幸的是,界面類可以方便的輸出日誌到界面,卻不可以直接發送和接收報文,而邏輯類恰恰相反,雖然把控着報文,對於輸出報文到界面卻是短腿。。。而我又不希望這兩個類互相包含頭文件。。。好吧,強迫症就是作==||。。。

經過小師傅的提點,我用回調函數解決了這個問題。大致如下:

首先在界面類中添加一個靜態的回調函數:

static void CALLBACK PrintLogCallback(void* pInstance, const char* str_log, int len);


千萬別小看第一個參數,他可是可以動用界面類中的非靜態兵力的制勝法寶啊!實現此函數的時候可以這樣寫:

void CCOM102Dlg::PrintLogCallback(void* pInstance, const char* str_log, int len)
{
    CCOM102Dlg* pCOM102Dlg = (CCOM102Dlg*) pInstance;
    //然後拿着pCOM102Dlg 這個兵符,你想幹啥幹啥去吧
    //比如你可以像下面這樣調用自定義的打印日誌方法:
    //pCOM102Dlg->AppendLog(str_log, len);
}


這樣完成第一步,第二步就是要在邏輯類中添加調用回調函數的接口,如下:

typedef void (CALLBACK *PrintLogCallback)(void* pInstance, const char* str_log, int len); 
//設置與界面類交互需要的回調函數,保存函數指針
void SetInterfaceEngine(void *pInstance, PrintLogCallback printLog);

//保存與回調函數有關信息的結構體
struct InterfaceEngine
{
    void *pInstance;
    PrintLogCallback printLog;
};
InterfaceEngine m_engine;

//邏輯類的打印日誌函數
void PrintLog(const char* buf, int len);


然後在需要用到界面類的打印日誌功能時,找你保存的函數指針替你辦事就可以啦!

void CClassLogic::SetInterfaceEngine(void *pInstance, PrintLogCallback printLog)
{
    m_engine.pInstance= pInstance;
    m_engine.printLog = printLog;
}


 

void CClassLogic::PrintLog(const char* buf, int len)
{
    if (m_engine.pInstance!= NULL)
    {
        m_engine.printLog(m_engine.pInstance, buf, len);
    }
}


 

所以現在只需要在界面類包含邏輯類的頭文件就可以了~聲明一個CClassLogic的成員變量m_cLogic,然後調用設置回調函數的接口就可以看到效果了:

    m_cLogic.SetInterfaceEngine(this, COM102Dlg::PrintLogCallback);


 

 

 

 

 

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章