在懂得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.
||
*/
在懂得BREW接口的原理之后, 那么该知道BREW接口是如何声明和实现了的更多相关文章
- 函数式 js 接口实现原理,以及 lodash/fp 模块
函数式 js 接口 之前在 youtube 上看到一个技术视频,讲“underscore.js的接口为什么不好用”,以及什么样的接口更好用.演讲者是 lodash.js 的作者,他提出了一种“全面函数 ...
- IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理
1.前言 一个安全的信息系统,合法身份检查是必须环节.尤其IM这种以“人”为中心的社交体系,身份认证更是必不可少. 一些PC时代小型IM系统中,身份认证可能直接做到长连接中(也就是整个IM系统都是以长 ...
- Mybatis接口编程原理分析(三)
前面两篇博客Mybatis接口编程原理分析(一)和Mybatis接口编程原理分析(二)我们介绍了MapperProxyFactory.MapperProxy和MapperMethod的操作及源码分析, ...
- Mybatis接口编程原理分析(二)
在上一篇博客中 Mybatis接口编程原理分析(一)中我们介绍了MapperProxyFactory和MapperProxy,接下来我们要介绍的是MapperMethod MapperMethod:它 ...
- 浅入浅出 Go 语言接口的原理
浅入浅出 Go 语言接口的原理 接口是 Go 语言的重要组成部分,它在 Go 语言中通过一组方法指定了一个对象的行为,接口 interface 的引入能够让我们在 Go 语言更好地组织并写出易于测试的 ...
- mybatis——mybatis打印sql 接口工作原理
https://blog.csdn.net/Lxinccode/article/details/79218566 接口工作原理: Dao接口即Mapper接口.接口的全限名,就是映射文件中的names ...
- Mybatis接口编程原理分析(一)
Mybatis接口编程示例 (1)首先定义接口编程需要的接口及其方法 public interface IUserMapper { public User getById(int id);//接口方法 ...
- 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 ...
- Ajax请求接口加密研究(针对网页前端的接口安全加密机制研究)
通常我们在h5前端调用后台接口时,一般是ajax,那么接口的安全成了一个问题. 这里可以肯定的说,前端调用的接口一定要验证! 然后剖析了微信网页版.京东网页版这些,也都是通过接口的形势绑定数据,所以在 ...
随机推荐
- ny20 吝啬的国度
吝啬的国度 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 在一个吝啬的国度里有N个城市,这N个城市间只有N-1条路把这个N个城市连接起来.现在,Tom在第S号城市,他有 ...
- Oracle PLSQL Demo - 09.Open、Fetch遍历游标[Open, Fetch, Close Record CURSOR]
declare r_emp scott.emp%rowtype; cursor cur_emp is select t.* from scott.emp t; begin open cur_emp; ...
- Can you share some Scala List class examples?
Scala List FAQ: Can you share some Scala List class examples? The Scala List class may be the most c ...
- electron-vue 项目搭建的地址
https://simulatedgreg.gitbooks.io/electron-vue/content/en/ 现在的网址:vue的electron的文件: https://github.com ...
- 使用jquery插件validate制作的表单验证案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- mysql sleep进程 过多
如果你没有修改过MySQL的配置,缺省情况下,wait_timeout的初始值是28800. wait_timeout过大有弊端,其体现就是MySQL里大量的SLEEP进程无法及时释放,拖累系统性能, ...
- BAT-给文件右击菜单增加7-ZIP浏览功能
Reg给文件右击菜单增加7-ZIP浏览功能 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\用7-ZIP浏览(ZJQ)] ...
- js中Math.round、parseInt、Math.floor和Math.ceil小数取整小结【转】
[摘要:之前常常正在代码中看到Math.round.parseInt.Math.floor战Math.ceil那四个函数,固然晓得效果皆能够返回一个整数,然则对他们四者的差别照样没有太清晰,本日便做一 ...
- 使用System.IO.Combine(string path1, string path2, string path3)四个参数的重载函数提示`System.IO.Path.Combine(string, string, string, string)' is inaccessible due to its protection level
今天用Unity5.5.1开发提取Assets目录的模块,使用时采用System.IO.Path.Combine(string, string, string, string)函数进行路径生成 明明是 ...
- rails rake 版本问题
rails rake 版本问题 通常情况下,如果我们电脑上同时装了不同版本的rake时,运行rake命令时会出错,如: rake db:migrate rake aborted! You have a ...