现在的位置: 首页 > 综合 > 正文

深入BREW接口机制

2013年12月13日 ⁄ 综合 ⁄ 共 2574字 ⁄ 字号 评论关闭

初学Brew时,最烦人的就是接口的定义了,一大堆的宏,让人不知所云。这里,将以helloworld为例,我们一步一步揭开它的神秘面纱。

1helloworld

typedef
struct
_helloworld
{

       AEEApplet      a ;          
//第一个成分必须是
AEEApplet

    AEEDeviceInfo  DeviceInfo;
// 设备信息

    IDisplay      *pIDisplay;  // 显示

    IShell        *pIShell;    //指向Shell

    //加入自己的变量

} helloworld;

 

2AEEApplet

看看, 什么是AEEApplet?

typedef
struct
_AEEApplet
AEEApplet;

struct
_AEEApplet

{

   DECLARE_VTBL(IApplet) 

   AEECLSID       clsID;  

   uint32         m_nRefs;                     // Applet
reference counter

       …… 
//
其它的变量定义

};

DECLARE_VTBL(IApplet)又是什么?

它的宏定义为:

#define DECLARE_VTBL(iname)      iname  
vt##iname;

解开后为 

struct
_AEEApplet

{

   IApplet vtApplet; 
//
定义了一个接口

   AEECLSID       clsID;  

   uint32         m_nRefs;                     // Applet
reference counter

       ……  //其它的变量定义

};

3IApplet

IApplet又是什么东东呢? 这个比较难找。

AEE.h中找到如下代码:

typedef
struct
_IApplet         IApplet;

#define INHERIT_IApplet(iname)
/

   INHERIT_IBase(iname);
/

   boolean  (*HandleEvent)(iname
*
po,
AEEEvent
evt,
uint16
wp,
uint32
dwp)

 

QINTERFACE(IApplet)

{

   INHERIT_IApplet(IApplet);

};

#define IAPPLET_AddRef(p)                    GET_PVTBL(p,IApplet)->AddRef(p)

#define IAPPLET_Release(p)                   GET_PVTBL(p,IApplet)->Release(p)

还是看看解开后是什么吧:

struct _IApplet {

  struct IAppletVtbl *pvt;

};

typedef struct IAppletVtbl IAppletVtbl;

struct IAppletVtbl

{

    uint32 (*AddRef) (IApplet*);

uint32 (*Release) (IApplet*);

boolean (*HandleEvent)(IApplet * po, uint16
evt, uint16 wp, uint32 dwp);

};

至于宏定义

#define IAPPLET_AddRef(p)                    GET_PVTBL(p,IApplet)->AddRef(p)

展开来后则成了:

#define IAPPLET_AddRef(p)  (IApplet 
*)p->pvt->AddRef(p)

我们看到IApplet的定义中,只有一个变量,是一个指向vbtl的指针,名字为pvt,这就解释了为什么我们的Applet也会有一个pvt指针啦。

因为helloworld的第一个变量为IApplet类型的,当我们对helloworld时行向上转型到IApplet时,我们就可以使用IApplet类型中定义的成员变量。可以理解成C++中的子类与父类的继承关系。

4AEEApplet_New

再回到AEEAppGen.c中,我们就能更好的理解AEEApplet_New的思想了。

在分配Applet内存时,分配的并不仅仅是helloworld结构内存的大小,而是helloworld结构大小再加上IappletVtbl的大小。

(AEEApplet*)MALLOC(nSize +
sizeof(IAppletVtbl))

从前面的定义我们知道IAppletVtbl实际是一组函数指针,给vtbl分配了内存后,就要对它进行初始化,让它指向实际的函数地址。这样才可以通过我们前面的宏定义来执行实际的函数:

#define
IAPPLET_AddRef(p)  (IApplet 
*)p->pvt->AddRef(p)

这一段代码如下:

   appFuncs =
(
IAppletVtbl
*)((
byte
*)
pme +
nSize);//appFuns指向实际分配的内存块

   //Initialize the
individual entries in the VTBL

   appFuncs->AddRef      = AEEApplet_AddRef;
//
实际的AddRef函数地址

   appFuncs->Release     = AEEApplet_Release;//实际的Release函数地址

   appFuncs->HandleEvent =
AEEApplet_HandleEvent;//实际的HandleEvent地址

下面就要对Iappletpvt进行符值,让它指向这一块vtbl啦。

   INIT_VTBL(pme,
IApplet,
*
appFuncs); 

宏展开再看看:

   ((IApplet *)pme)->pvt = (IAppletVtbl
*)appFuncs;

5.结论:

由此我们可以看到,在brew中,所谓的接口,不过是一个指针,不同类型的接口的指针指向了不同的的函数指针表,可以理解为C++中的Vtbl

对于某一接口的的实例,它的第一个变量就是指向Vtbl的指针,一般来讲,它的Vtbl表是存在这个类实例的尾端的。在这个类初始化时,必须同时对它的vtbl也要初始化。

抱歉!评论已关闭.