终于写到c++的非侵入式接口了,兴奋,开心,失望,解脱,…… 。在搞了这么多的面向对象科普之后,本人也已经开始不耐烦,至此,不想做太多阐述。

虽然,很早就清楚怎么在c++下搞非侵入式接口,但是,整个框架代码,重构了十几次之后,才终于满意。支持给基本类型添加接口,好比int,char,const char*,double;支持泛型,好比vector,list;支持继承,基类实现的接口,表示子类也继承了对该接口的实现,而且子类也可以拒绝基类的接口,好比鸭子拒绝基类鸟类“会飞”,编译时报错;支持接口组合;……,但是,这里仅仅简单介绍其原理,并不涉及C++中各种变态细节的处理,C++中,但凡是要正儿八经的稍微做点正事,就要面临无穷无尽的细节纠结。

先看看其使用例子:

1、自然是定义一个接口:取之于真实代码片段

struct IFormatble

{

static TypeInfo* GetTypeInfo();

virtual void Format(TextWriter& stream, const FormatInfo& info) = 0;

virtual bool Parse(TextReader& stream, const FormatInfo& info)

{

PPNotImplement();

}

};

2、接口的实现类,假设为int添加IFormatble的接口实现,实际代码肯定不会这样对一个一个的基本类型来写实现类的代码。这里只是为了举例说明。类的名字就随便起好啦,

struct ImpIntIFormatble : IFormatble

{

int* mThis; //这一行是关键

virtual void Format(TextWriter& stream, const FormatInfo& info)override

{}

virtual bool Parse(TextReader& stream, const FormatInfo& info)override

{}

};

这里的关键是,实现类的字段被规定死了,最多只能包含3个指针成员字段,且第1个字段一定是目的类型指针,第二是类型信息对象(用于泛型),第三是额外参数,次序不能乱。成员字段如果不需要用到第二第三个成员字段数据,可以省略不写,好比这里。所有接口实现类必须遵守这样的内存布局;

3、装配,将接口的实现类装配到现有的类上,以告诉编译器该类对于某个接口(这里为IFormatble)的实现,用的是第2步的实现类ImpIntIFormatble;

PPInterfaceOf(IFormatble, int, ImpIntIFormatble)

4、将实现类注册到类型信息的接口实现列表中,这一步可以省略,只是为了运行时的接口查询,相当于IUnknown的Query。这一行代码是在全局对象的构造函数中执行的,放在cpp源文件中

RegisterInterfaceImp();

然后就可以开开心心地使用接口了,比如

int aa = 20;

TextWriter stream();

FormatInfo info();

TInterface formatable(aa); //TInterface这个名字过难看,也没办法了

formatable->Format(stream, info);

double dd = 3.14;

formatable = TInterface(dd); //假设double也实现IFormatble

formatable->Format(stream, info);

是否有点神奇呢?其实也没什么,不过就是在trait和内存布局上做文章,也就只是用了类型运算的伎俩。考察ImpIntIFormatble的内存布局,对于普遍的c++编译器来说,对象的虚函数表指针(如果存在的话),都放在对象的起始地址上,后面紧跟对象本身的成员数据字段,因此,ImpIntIFormatble的内存布局相当于,

struct ImpIntIFormatble

{

void* vtbl;

int* mThis;

};

注意,这里已经没有继承了。这就是,实现了IFormatble 接口的ImpIntIFormatble对象的内存表示。因此,可以想象,所有的接口实现类的内存布局都强制规定为以下形式:

struct InterfaceLayout

{

const void* mVtbl;

const void* mThis; //对象本身

const TypeInfo* mTypeInfo; //类型信息

const void* mParam; //补充参数,一般很少用到

};

当然,如果编译器的虚函数表指针不放在对象起始地址的话,就没法这么玩了,那么非侵入式接口也无从做起。然后,就是TInterface了,继承于InterfaceLayout

template

struct TInterface : public InterfaceLayout

{

typedef IT interface_type;

static_assert(is_abstract::value, "interface must have pure function");

static_assert(sizeof(IT) == sizeof(void*), "Can't have data");

public:

interface_type* operator->()const

{

interface_type* result = (interface_type*)(void*)this;

return result;

}

};

不管怎么说都好,TInterface对象的内存布局与接口实现类的内存布局一致。因此操作符->重载函数才可以粗暴的类型转换来顺利完成。然后构造TInterface对象的时候就是强制获取ImpIntIFormatble对象的虚函数表(也就是其起始地址的指针数据)指针赋值给InterfaceLayout的mVtbl,进而依次把实际对象的指针放在mThis上,获取到类型信息对象放在mTypeInfo中,如果有必要搭理mParam,也相应地赋值。

然后,就是templatestruct InterfaceOf各种特化的运用而已,就不值一提了。

由于c++的abi没有统一标准,并且,c++标准也没有规定编译器必须用虚函数表来实现多态,所以,这里的奇技淫巧并不能保证在所有平台上都能够成立,但是,非侵入式接口真是方便,已经是本座写c++代码的核心工具,一切都围绕着非侵入式接口来展开。

原本打算长篇大论,也只有草草收场。之后,本座就解放了,会暂时离开cppblog很久,计划中的内容,消息发送,虚模板函数,字符串,输入输出,格式化,序列化, locale,全局变量,模板表达式,组合子解析器,allocator,智能指针,程序运行时,抽象工厂访问者等模式的另类实现,以求从全新的角度上来表现C++的强大,也只能中断了。

java非侵入式接口实现,C++编写非侵入式接口相关推荐

  1. wxWidgets:编写非英语应用程序

    wxWidgets:编写非英语应用程序 wxWidgets:编写非英语应用程序 语言环境 源代码中的非英语字符串或 8 位字符 字体映射 转换数据 帮助文件 wxWidgets:编写非英语应用程序 本 ...

  2. [基础题] 5.(*)按如下要求编写Java应用程序:(1)编写一个用于表示战斗能力的接口Fightable,

    /*5.(*)按如下要求编写Java应用程序: (1)编写一个用于表示战斗能力的接口Fightable, 该接口包含:整型常量MAX: 方法void win(),用于描述战斗者获胜后的行为: 方法in ...

  3. jdbc是java语言编写的类和接口_JDBC——Java语言连接数据库的标准

    JDBC概述 API JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Jav ...

  4. 按要求编写一个Java应用程序:(1)编写一个矩形类Rect,包含:两个属性...2、定义一个接口Shape,包括计算图形面积(getArea)和周长(getcir)...

    1.按要求编写一个Java应用程序: (1)编写一个矩形类Rect,包含:两个属性:矩形的宽width和高height:一个带有两个参数的构造方法,用于将width和height属性初化:两个方法:求 ...

  5. java 泛型示例_Java泛型示例教程–泛型方法,类,接口

    java 泛型示例 Java Genrics is one of the most important features introduced in Java 5. Java Genrics是Java ...

  6. 夯实Java基础系列6:一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别!

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  7. 通过接口给fragment传值_Dataway 4.1.3 发布,开发一个接口不必在编写任何形式的代码,秒级交付!...

    Dataway 数据接口配置服务 依托 DataQL 服务聚合能力,为应用提供一个 UI 界面.并以 jar 包的方式集成到应用中. 通过 Dataway 可以直接在界面上配置和发布接口. 这种模式的 ...

  8. java多线程中 锁 的概念的理解,java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一)...

    目前对于同步,仅仅介绍了一个关键字synchronized,可以用于保证线程同步的原子性.可见性.有序性 对于synchronized关键字,对于静态方法默认是以该类的class对象作为锁,对于实例方 ...

  9. java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一)

    java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一) 目前对于同步,仅仅介绍了一个关键字synchronized,可以用于保证线程同步的原子性.可见性.有序性 对于synchronize ...

最新文章

  1. 组合数据类型练习、英语词频统计
  2. mysql问题定位_十、MySQL的SQL优化之定位SQL的问题 - 系统的撸一遍MySQL
  3. node.js 代码修改 自动识别重启工具
  4. 买书(信息学奥数一本通-T1293)
  5. SPS :SPS 2003 安装过程中的语言版本问题。
  6. Python中的进程和线程
  7. VB后台获得按键,并执行自己的函数(非钩子及热键)
  8. sprintf函数、snprintf函数、asprintf函数、vsprintf
  9. 骑行在路上的IT两年
  10. 数据结构-约瑟夫问题课后作业
  11. 记录MS VisualStudio添加注释宏代码写法
  12. 分享PMP真题回忆,出征软考高级项目管理师
  13. docker镜像命令
  14. python正交表结果生成
  15. mysql 保存富文本的类型_富文本编辑器内容在mysql数据库以什么类型保..._网络编辑_帮考网...
  16. alert的确定和取消
  17. 数学物理方法·复数/模/辐角引导题目
  18. 如果一觉醒来已是光年之远
  19. U盘数据被格式化怎么办,U盘格式化数据如何恢复?
  20. 秀!学妹看见都惊呆的 Python 小项目!【界面应用项目及游戏开发】

热门文章

  1. matlab中for循环的步长
  2. 自编码 Autoencoder
  3. lua运行外部程序_Lua 协同程序(coroutine)
  4. MySQL 数据库“十宗罪”(十大经典错误案例)
  5. 那些年,我们处理过的SQL问题
  6. Open Harmony移植:build lite编译构建过程
  7. CPU高速缓存与极性代码设计
  8. NanoDet:这是个小于4M超轻量目标检测模型
  9. 六个步骤,从零开始教你搭建基于WordPress的个人博客
  10. 论软件工程师的自我修养:角色、重构与质量