参考: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.

||

*/

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

  1. 函数式 js 接口实现原理,以及 lodash/fp 模块

    函数式 js 接口 之前在 youtube 上看到一个技术视频,讲“underscore.js的接口为什么不好用”,以及什么样的接口更好用.演讲者是 lodash.js 的作者,他提出了一种“全面函数 ...

  2. IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理

    1.前言 一个安全的信息系统,合法身份检查是必须环节.尤其IM这种以“人”为中心的社交体系,身份认证更是必不可少. 一些PC时代小型IM系统中,身份认证可能直接做到长连接中(也就是整个IM系统都是以长 ...

  3. Mybatis接口编程原理分析(三)

    前面两篇博客Mybatis接口编程原理分析(一)和Mybatis接口编程原理分析(二)我们介绍了MapperProxyFactory.MapperProxy和MapperMethod的操作及源码分析, ...

  4. Mybatis接口编程原理分析(二)

    在上一篇博客中 Mybatis接口编程原理分析(一)中我们介绍了MapperProxyFactory和MapperProxy,接下来我们要介绍的是MapperMethod MapperMethod:它 ...

  5. 浅入浅出 Go 语言接口的原理

    浅入浅出 Go 语言接口的原理 接口是 Go 语言的重要组成部分,它在 Go 语言中通过一组方法指定了一个对象的行为,接口 interface 的引入能够让我们在 Go 语言更好地组织并写出易于测试的 ...

  6. mybatis——mybatis打印sql 接口工作原理

    https://blog.csdn.net/Lxinccode/article/details/79218566 接口工作原理: Dao接口即Mapper接口.接口的全限名,就是映射文件中的names ...

  7. Mybatis接口编程原理分析(一)

    Mybatis接口编程示例 (1)首先定义接口编程需要的接口及其方法 public interface IUserMapper { public User getById(int id);//接口方法 ...

  8. 35.按要求编写Java程序: (1)编写一个接口:InterfaceA,只含有一个方法int method(int n); (2)编写一个类:ClassA来实现接口InterfaceA,实现int method(int n)接口方 法时,要求计算1到n的和; (3)编写另一个类:ClassB来实现接口InterfaceA,实现int method(int n)接口 方法时,要求计算n的阶乘(n

      35.按要求编写Java程序: (1)编写一个接口:InterfaceA,只含有一个方法int method(int n): (2)编写一个类:ClassA来实现接口InterfaceA,实现in ...

  9. Ajax请求接口加密研究(针对网页前端的接口安全加密机制研究)

    通常我们在h5前端调用后台接口时,一般是ajax,那么接口的安全成了一个问题. 这里可以肯定的说,前端调用的接口一定要验证! 然后剖析了微信网页版.京东网页版这些,也都是通过接口的形势绑定数据,所以在 ...

随机推荐

  1. nyoj228 士兵杀敌(5)插线问线

    士兵杀敌(五) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5   描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为0~M,每次有任务的时候,总会有一批编号连在一起人请战 ...

  2. 如何让在Html中特殊字符被数据加载时对于html标签的自动转义 补充

    1.将此过滤器添加到ng-bind-html所绑定的数据中,便实现了在数据加载时对于html标签的自动转义. <div ng-repeat="item in list" &g ...

  3. 将struts的jar包拷贝到WEB-INF/lib导致eclipse中配置好的javadoc失效

    我通过这个步骤导入了struts的jar包并且配置好了javadoc,且亲测可用: http://www.cnblogs.com/qrlozte/p/3173805.html 但是当我把struts的 ...

  4. if的另一个实现思路

    在一些场景中需要根据根据一个传入的额值来做不同的处理,而且if有很多层,此时如果一直写if代码就会雍容.一种比较好的方法就是写一个map列出与if对应的情况,然后map的value就能放一些方法或者其 ...

  5. 【Unity笔记】将角色的碰撞体朝向鼠标点击方向——角色朝向鼠标

    int floorMask; // 自动寻路层 void Awake() { floorMask = LayerMask.NameToLayer("Floor"); } void ...

  6. am335x PDK3.0 设置为单网口配置记录

    原来的配置是双网口的,现在要配置为单网口. 一直以为这个配置是在 make menuconfig 里面, 没想到是在设备树里面. 修改设备树 // vim arch/arm/boot/dts/am33 ...

  7. maven_nexus3私服搭建

    [maven_nexus3私服搭建] # 00.安装环境说明# (1)Windows7 64位# (2)JDK1.8 64位# (3)Sonatype Nexus Repository OSS 3.1 ...

  8. Winform控件:保存文件对话框(SaveFileDialog)

    SaveFileDialog用于保存文件 1.新建Winform窗体应用程序,命名为SaveFileDialogDemo. 2.在界面上添加一个按钮的控件(用于打开保存文件对话框),添加文本控件,用于 ...

  9. java-事务-案例

    项目结构: 数据库: /* SQLyog Ultimate v12.09 (64 bit) MySQL - 5.5.53 : Database - threadlocal ************** ...

  10. MongoDB · 引擎特性 · MongoDB索引原理

    MongoDB · 引擎特性 · MongoDB索引原理数据库内核月报原文链接 http://mysql.taobao.org/monthly/2018/09/06/ 为什么需要索引?当你抱怨Mong ...