assert(wrapper->add(key,10,data,20));
11 free(key);
12 free(data);
13 releaseMCWrapper(wrapper);
14 printf("All Over.\n");
15 return 0;
16 }
三、原理分析:
该技巧有些类似于Windows中的COM技术,通过编译器生成的C++虚拟表,以达到这种跨编译器的二进制接口形式。该技巧有以下几个注意事项:
1. 定义纯虚接口,该接口中不能定义任何成员变量,否则在编译器之间无法做到二进制兼容。
2. 定义C接口形式的工厂方法createMCClient(),用于创建实际的子类,这样使用者便可以通过动态链接的方式加载该库,如Windows中LoadLibrary和Linux中的dlopen。
3. 定义C接口的对象指针释放方法releaseMCClient(),在接口中已经将接口的析构方法定义为protected类型,因此调用者无法直接delete该指针,而是必须通过该接口指针导出的release()方法或者该C导出函数来释放,尽管两者都可以达到释放资源的目的,然而我们在实践中还是推荐使用该C导出函数,以便保证使用方式的一致性和未来的扩展性。
4. 在定义纯虚接口所在的头文件中不要包含任何和实现细节相关的信息。
总之,一切都是这样的美好,我对我的设计也是非常自信,甚至有一点点的得意,因为我曾经使用该方式完成了很多库在C++ Builder和VC++之间的迁移,从而达到这种跨编译器的效果。可以想象,就连极为复杂的webkit也被我在短短的一周内成功迁移,libmemcached应该是不在话下的。然而事实却是残酷的,我的VC测试用例无法正常调用该纯虚接口,每次都会报出和寄存器相关的错误。这让我立刻想到了之前代码中存在的问题,一定是我在声明纯虚接口时,没有为每个接口函数明确声明调用规范,而gcc和VC++的缺省调用规范恰恰又是不同的(VC++缺省为cdcel)。想到这里,我立即做出修改,将所有接口函数的调用规范都指定为cdcel,然后用gcc重新编译该Wrapper库。结果如何呢?请看下篇
作者 Stephen_Liu