天天看點

簡單的c++回調函數設計方法

簡單的c++回調函數實作辦法:

    最近為公司更新schematic編輯器,GUI部分采用QT來實作,為了防止GUI采用的開發庫的變化,決定采用

GUI+Engine的辦法實作,GUI部分用Qt來實作,Engine使用C++來實作作為library,gui運作時連結engine庫。Qt提供signal/slot機制可以提供比回調函數更友善的通訊方式,此處就不再贅述。此處簡單介紹一下engine和Gui層的回調函數實作辦法。

    在項目開發中發現很多地方需要通過回調來實作,實作辦法有很多種,例如函數指針,c++的虛函數,boost 的functor仿函數等,這裡提供一種利用c++模闆來實作的辦法,下面繪制來作為例子,library提供畫線,畫點,畫矩形,畫字元串的接口。

    先介紹一下其它的實作辦法:

    1: 函數指針:

    了解linux kernel對裝置驅動管理的對函數指針肯定不陌生:

     library中定義接口

     struct RGB{

unsigned int r;

unsigned int g;

unsigned int b;

};

typedef struct _drawFp_{

int (*drawPoint)(int x,int y,RGB rgb);

int (*drawLine)(int sx,int sy,int ex,int ey,RGB rgb);

int (*drawRect)(int left,int top,int width,int height,bool fill,RGB rgb);

int (*drawString)(const char *str,int x,int y,int font,RGB rgb);

}drawFPointer;

     這樣在GUI層中需要實作對應的四個函數(注意參數和傳回值的一緻性):

     int gui_drawPoint(int x,int y,RGB rgb)

{

//painter

}

int gui_drawLine(int sx,int sy,int ex,int ey,RGB rgb)

{

//painter

}

int gui_drawRect(int left,int top,int width,int height,bool fill,RGB rgb)

{

//painter

}

int gui_drawString(const char *str,int x,int y,int font,RGB rgb)

{

//painter

}

/* 定義drawFPoint */

drawFPointer gui_drawFPoint = {

gui_drawPoint,

gui_drawLine,

gui_drawRect,

gui_drawString,

};

     把這個gui_drawFPoint傳遞給library即可。

     2:虛拟函數

    在library中實作接口

     class CDrawFPoint

{

public:

CDrawFPoint();

~CDrawFPoint();

virtual int drawPoint(int x,int y,RGB rgb) = 0;

virtual int drawLine(int sx,int sy,int ex,int ey,RGB rgb) = 0;

virtual int drawRect(int left,int top,int width,int height,bool fill,RGB rgb) = 0;

virtual int drawString(const char *str,int x,int y,int font,RGB rgb) = 0;

};

     在gui中實作繼承此類

     class GuiCDrawFpoint : public CDrawFPoint

{

public:

GuiCDrawFpoint(){}

~GuiCDrawFpoint(){}

int drawPoint(int x,int y,RGB rgb){/*painter */return 1;}

int drawLine(int sx,int sy,int ex,int ey,RGB rgb){return 1;}

int drawRect(int left,int top,int width,int height,bool fill,RGB rgb){return 1;}

int drawString(const char *str,int x,int y,int font,RGB rgb){return 1;}

};

    同樣把執行個體化指針傳遞給library即可,當然gui中也可以直接繼承此類。

    3: stl & boost functor

    stl 和 boost functor提供更強大的功能,不過受制于參數個數的限制。

    在前面的1,2中方法裡面,可以根據自己的需求定義參數的個數,和函數指針或者成員函數的數量,但是無法

    直接把gui層中的成員函數進行幫頂和回調,下面介紹一種模闆的實作辦法

    4:手動寫模闆

    在libraray層中:

     template <class T> class IdrawPoint : public CDrawFPoint

{

public:

typedef int (T::*DRAW_POINT)(int x,int y,RGB rgb);

typedef int (T::*DRAW_LINE)(int sx,int sy,int ex,int ey,RGB rgb);

typedef int (T::*DRAW_RECT)(int left,int top,int width,int height,bool fill,RGB rgb);

typedef int (T::*DRAW_STRING)(const char *str,int x,int y,int font,RGB rgb);

IdrawPoint(T *obj,DRAW_POINT drawPoint,DRAW_LINE drawLine,DRAW_RECT drawRect,DRAW_STRING drawString)

{

m_guiObj = obj;

m_drawPoint = drawPoint;

m_drawLine = drawLine;

m_drawRect = drawRect;

m_drawString = drawString;

}

/* 實作純虛函數*/

int drawPoint(int x,int y,RGB rgb){

return (m_guiObj->*m_drawPoint)(x,y,rgb);

}

int drawLine(int sx,int sy,int ex,int ey,RGB rgb)

{

return (m_guiObj->*m_drawLine)(sx,sy,ex,ey,rgb);

}

int drawRect(int left,int top,int width,int height,bool fill,RGB rgb)

{

return (m_guiObj->*m_drawRect)(left,top,width,height,fill,rgb);

}

int drawString(const char *str,int x,int y,int font,RGB rgb)

{

return (m_guiObj->*m_drawString)(str,x,y,font,rgb);

}

private:

T *m_guiObj;

DRAW_POINT m_drawPoint;

DRAW_LINE m_drawLine;

DRAW_RECT m_drawRect;

DRAW_STRING m_drawString;

};

在library的接口類中定義

void accept(class CDrawFPoint *fp){m_fp = fp;}

CDrawFPoint *m_fp;  

    在gui層中可以在類的成員函數中直接實作CDrawFPoint 的接口而不需要繼承此類

    class MyGuiLevel{

public:

MyGuiLevel();

~MyGuiLevel();

void init();

int drawPoint(int x,int y,RGB rgb){/*painter */return 1;}

int drawLine(int sx,int sy,int ex,int ey,RGB rgb){return 1;}

int drawRect(int left,int top,int width,int height,bool fill,RGB rgb){return 1;}

int drawString(const char *str,int x,int y,int font,RGB rgb){return 1;}

};

void MyGuiLevel::init()

{

/* 此處執行個體化模闆*/

IdrawPoint<MyGuiLevel> *guiDrawFPoint;

guiDrawFPoint = new IdrawPoint<MyGuiLevel>(this,&MyGuiLevel::drawPoint,

&MyGuiLevel::drawLine,

&MyGuiLevel::drawRect,

&MyGuiLevel::drawString);

/* accept(guiDrawFPoint) */

}   

     這種設計辦法library無需關心gui層的具體任何實作,此處利用了虛拟函數和模闆的的辦法實作了

    将一個成員函數直接注冊給底層的辦法。