深入分析D语言接口与COM接口的关系

前两天为了解决dxpcom项目中遇到的xpcom接口兼容性问题,看了一下DMD编译器的源码,对D的接口有了一些了解,现在总结出来,备忘。

D中有了专门用于标识接口的关键字interface,而不用象C++中使用抽象类来代替。
interface ITest
{
int test();
}

class ITest
{
int test()=0;
}


而D中的接口与C++中的接口不同之处是,D中的接口仍然含有ClassInfo,存放在虚表的0项上。

从DMD的源码中可以得知,D中的类,接口都在虚表的0项上保存了ClassInfo指针。
这样,D中的接口是无法与C++接口兼容的,则D就无法调用Windows的COM对象,至少是无法“优雅”的调用(仍然可以使用struct进行二进制兼容代替)。

为了解决这个问题,DMD就需要能够表示出与C++兼容的COM接口,即需要一个虚表是"干净"的接口。又由于,从一个COM接口继承的接口仍然是一个COM接口,而COM模型的实现上又恰好定义了一个“IUnknown”根接口(COM体系中的所有的接口都是继承了IUnknown)。

所以,出于简单实现的原则,DMD区分一个接口是D接口还是COM接口,关键就是判断这个接口是不是叫做IUnknown,以及这个接口是否继承自IUnknown,虽然接口都是通过Interface关键字声明。更有趣的是,DMD仅仅判断接口的名字是否为"IUnknown"而根本不管接口中的方法如何定义。

以上所述内容在进行Windows COM编程时,几乎不会被察觉,因为Windows的所有接口都是继承自IUnknown,只要正常使用就可以了。

而在进行Mozilla xpcom编程的时候,xpcom的根接口叫做ISupports,DMD根本就不会认为这是需要编译为C++兼容的COM接口,而仍然会将虚表的0项进行保留,结果给使用者造成了虚表指针偏移了的印象。

基于D的这个识别COM接口的方式,在dxpcom项目中,qiezi使用了别名的方式进行了变换,既将dxpcom项目中的所有的接口名称进行了优雅的统一,又能够使DMD生成正确的COM接口:
extern(Windows)
interface IUnknown {
static const char[] IID_STR = NS_ISUPPORTS_IID_STR;
static const nsIID IID = NS_ISUPPORTS_IID;

/* void QueryInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
nsresult QueryInterface(nsIID * uuid, void * *result);

/* [noscript, notxpcom] nsrefcnt AddRef (); */
nsrefcnt AddRef();

/* [noscript, notxpcom] nsrefcnt Release (); */
nsrefcnt Release();

}

alias IUnknown nsISupports;


这个现象同时也很好的说明了,D中的别名(alias)在符号的处理方面仅仅是一个符号的替换,同C/C++中的#define的作用相同。

下面的两段代码就能很好的诠释本文的内容(感谢qiezi提供)

代码一,无法通过运行期断言,因为接口IInterface仍然为标准D接口,虚表的0项为ClassInfo指针无法被显示的调用,在执行的结果中就表现为虚表进行了偏移。
extern(Windows):   
int test1(IInterface p)
{
return 1;
}

int test2(IInterface p)
{
return 2;
}

int test3(IInterface p)
{
return 3;
}

struct InterfaceVtbl
{
extern(Windows):
int function(IInterface) test1;
int function(IInterface) test2;
int function(IInterface) test3;
}

struct Interface
{
InterfaceVtbl* vtbl;

InterfaceVtbl vtbl_;

static Interface opCall()
{
Interface res;
res.vtbl_.test1 = &test1;
res.vtbl_.test2 = &test2;
res.vtbl_.test3 = &test3;
res.vtbl = &res.vtbl_;
return res;
}
}

interface IInterface
{
int test1();
int test2();
int test3();
}

extern (D):

void main()
{
Interface i = Interface();
assert(i.vtbl.test1(cast(IInterface)&i) == 1);
assert(i.vtbl.test2(cast(IInterface)&i) == 2);
assert(i.vtbl.test3(cast(IInterface)&i) == 3);

IInterface ii = cast(IInterface)&i;
assert(ii.test1() == 1);
assert(ii.test2() == 2);
assert(ii.test3() == 3);
}


代码二,与代码一的结构完全一致,却能够通过运行时断言的检查。唯一的不同仅仅是IInterface的名字换成了IUnknown!!
extern(Windows):   
int test1(IUnknown p)
{
return 1;
}

int test2(IUnknown p)
{
return 2;
}

int test3(IUnknown p)
{
return 3;
}

struct InterfaceVtbl
{
extern(Windows):
int function(IUnknown) test1;
int function(IUnknown) test2;
int function(IUnknown) test3;
}

struct Interface
{
InterfaceVtbl* vtbl;

InterfaceVtbl vtbl_;

static Interface opCall()
{
Interface res;
res.vtbl_.test1 = &test1;
res.vtbl_.test2 = &test2;
res.vtbl_.test3 = &test3;
res.vtbl = &res.vtbl_;
return res;
}
}

interface IUnknown
{
int test1();
int test2();
int test3();
}

extern (D):

void main()
{
Interface i = Interface();
assert(i.vtbl.test1(cast(IUnknown)&i) == 1);
assert(i.vtbl.test2(cast(IUnknown)&i) == 2);
assert(i.vtbl.test3(cast(IUnknown)&i) == 3);

IUnknown ii = cast(IUnknown)&i;
assert(ii.test1() == 1);
assert(ii.test2() == 2);
assert(ii.test3() == 3);
}


另外需要说明的是extern(D),extern(Windows),extern(Pascal)等特征,只是用来描述函数的调用约定,与接口的类型无关。
一句话:D中的类与标准D接口都有ClassInfo在虚表的0项上,而COM接口的虚表是干净的;而将一个接口声明为COM接口的方式为:将这个接口命名为IUnknown或继承自IUnknown。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值