天天看点

简单的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层的具体任何实现,此处利用了虚拟函数和模板的的办法实现了

    将一个成员函数直接注册给底层的办法。