在懂得BREW接口的原理之后, 那么该知道BREW接口是如何声明和实现了

 

参考:http://blog.csdn.net/peteryxk/article/details/1584514

首先介绍几个用到的宏定义:

 

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.

 

   ||

 

*/

 

原文链接: https://www.cnblogs.com/silentjesse/p/3232946.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    在懂得BREW接口的原理之后, 那么该知道BREW接口是如何声明和实现了

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/98395

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月10日 上午4:41
下一篇 2023年2月10日 上午4:43

相关推荐