BREW中的接口声明及使用
首先介绍几个用到的宏定义: l #define VTBL(iname) iname##Vtbl 例:VTBL(IWindow)将被替换为 IWindowVtbl。 从名字的后缀可以看出,它是模拟C++的虚函数的函数表。表中的每一项代表了一个函数指针。通过给指针赋予不同的值,便可以得到同一接口的不同实现。 l #define AEEVTBL(iname) iname##Vtbl 该宏的作用和第一个宏完全一样。唯一不同的是第一个宏用于第一种风格的接口声明,而AEEVTBL用于第二种风格的接口的声明。见下。 l #define INHERIT_IBase(iname) / uint32 (*AddRef) (iname*);/ uint32 (*Release) (iname*) 例:INHERIT_IBASE(IWindow)将被替换为: Uint32 (*AddRef) ( IWindow*) ; Uint32 (*Release) (IWindow*) 从名字中可以看出,它是用来管理组件的生命周期的函数,回忆回忆COM的生命周期管理函数。 一,第一种风格的接口 1, 接口的声明 #define QINTERFACE(iname) struct _##iname {/ struct VTBL(iname) *pvt;/ };/ typedef struct VTBL(iname) VTBL(iname);/ struct VTBL(iname) 例:QINTERFACE(IWindow)将被替换为: Struct _IWindow { Struct IWindowVtbl *pvt ; } ; typedef struct IWindowVtbl IWindowVtbl ; struct IWindowVtbl 如果在该声明的后面追加函数指针,如: QINTERFACE(IWindow) { INHERIT_IBASE(IWindow) ; }; 那么它将被替换为: Struct _IWindow { Struct IWindowVtbl *pvt ; } ; typedef struct IWindowVtbl IWindowVtbl ; struct IWindowVtbl { Uint32 (*AddRef) ( IWindow*) ; Uint32 (*Release) (IWindow*) } ; 由于此时IWindow并没有被声明为类型,所以在此之前需要它的类型定义。在下面的接口使用中,如果要组合该接口,那么需要IWindow的一个实例,所以IWindow的类型定义如下: typedef struct _IWindow IWindow; 2, 接口的使用 如下为该接口的实现: Struct WindowImpl { IWindow vtIWindow; Int nRef ; … } ; _IWindow是一个结构体,该结构体的唯一成员为指向虚函数表IWindowVtbl的指针pvt。 有一个专门的宏用来访问该虚函数表指针。 #define GET_PVTBL(p,iname) ((iname*)p)->pvt WindowImpl都是通过malloc等在heap上分配的。 pObj = malloc ( sizeof( WindowImpl ) + sizeof( IWindowVtbl ) ) ; pObj->pvt = pObj + 1 ; 即分配空间时,分配的大小为实现类的结构体大小加虚函数表体的大小,并设置虚函数表指针指向虚函数表。 当调用AddRef函数时,可以使用如下方法: GET_PVTBL(pObj, IWindow)->AddRef(); 它被替换为: ((IWindow*)pObj)->pvt->AddRef(); 能够正确的调用AddRef函数。 为了方便起见,一般定义如下的宏: #define IWINDOW_AddRef(p) Get_PVTBL(p, IWindow)->AddRef() 二,第二种风格的接口 1, 接口的声明 #define AEEINTERFACE(iname) / typedef struct AEEVTBL(iname) AEEVTBL(iname); / struct AEEVTBL(iname) 如果声明了: AEEINTERFACE(IWindow) { INHERIT_IBASE(IWindow) ; }; 那么将被替换为: typedef struct IWindowVtbl IWindowVtbl ; struct IWindowVtbl { Uint32 (*AddRef) ( IWindow*) ; Uint32 (*Release) (IWindow*) ; } ; 同样在此之前,应首先声明IWindow。 更被推荐的做法如下: l 首先进行宏定义INHERIT_XXX 例如: #define INHERIT_IWindow(IWindow) INHERIT_IBASE(IWindow) l 然后使用AEEINTERFACE_DEFINE定义类型 它的宏定义如下: #define AEEINTERFACE_DEFINE(iname)/ typedef struct iname iname;/ AEEINTERFACE(iname) {/ INHERIT_##iname(iname);/ } 这样AEEINTERFACE_DEFIN(IWindow)将被替换为: typedef struct IWindow IWindow ; typedef struct IWindowVtbl IWindowVtbl ; struct IWindowVtbl { Uint32 (*AddRef) ( IWindow*) ; Uint32 (*Release) (IWindow*) ; } ; 注:如果仅仅有如下的类型定义: typedef truct Dummy Dummy ; 那么程序中有如下的语句的话:Dummy * pDummy ; 是合法的。 但是 Dummy dummy将是不合法的。因为Dummy是不完整的类型,所以不能实例化。但是可以存在指向不完全类型的指针。 上述接口就是利用了这一点,它会在函数的内部将指针牵制类型转换为实现类的指针。 2, 接口的使用 如下为该接口的实现: Struct WindowImpl { AEEVTBL(IWindow) *pvt; Int nRef ; … } ; pvt是指向IWindowVtbl的指针,即虚函数表指针。 相应的宏为:#define AEEGETPVTBL(p,iname) (*((AEEVTBL(iname) **)((void *)p))) 假如pObj为指向WindowImpl的指针。那么AEEGETPVTBL(pObj,IWindow)将被替换为 (*(IWindowVtbl**)(void*)pObj),此表达式的结果就是指向IWindowVtbl结构体的指针。 AEEGETPVTBL(pObj,IWindow)->AddRef();便完成了函数的调用。 一般写一个辅助宏: #define IWINDOW_AddRef(p) AEEGETPVTBL(p,IWindow)->AddRef() 三,两种风格间的比较 第一种风格,为了在实现类中保存vtbl的指针,必须借助于具体的类型IWindow。而在第二种风格中,IWindow仅仅是个不完整的类型定义。具体的在AeeInterface.h中有如下文字: /* || || There's a problem with QINTERFACE(): it wastes namespace and forces || implementers to choose a name for a type that's unnecessarily || different from the interface name, since the QINTERFACE macro || makes an interface into a concrete type with only one member(!), || the vtable. || || The macros below have all the same type-safety of the QINTERFACE() || macros, but don't force a concrete type on the object, allowing || implementers of interfaces to use the same type names, thus || obviating the need to do a type cast on entry to a method. || || It should be easy to redefine QINTERFACE and its descendants || in terms of the below. || |