先说结论:纯C接口兼容的不错,C++类可以通过纯虚类实现兼容(但是只能说大概率),其它的基本不要想,包括模板。
引自:https://www.cnblogs.com/lsgxeva/p/12151772.html
-------------------
两个编译器的c++ abi不兼容的,所以无法识别对方导出的符号完成链接。(啥事ABI看文章后半段)
但是可以用c语言中转,c的abi在所有编译器中都是相同的。
简单来说,把vc动态库所有导出接口都改为extern "C"形式即可,这样生成的动态库,MinGW是可以正确链接使用的,反之亦然。
但注意要规避一个问题,不要跨越动态库边界分配/释放内存,因为两边用的并不是同一套malloc/free。
并且发布程序时,两边的依赖都要带齐,比如vc库依赖的msvcrt等,mingw程序依赖的libpthread等。
关于c++对象,可以为其定义由纯虚函数组成的接口类,用c接口构造并返回接口指针,通过接口指针调用方法,这样的操作是没问题的。
不过虚接口的方式,其实是依赖了编译器的虚表结构,并没有在语言层面保证一定可用,最保险的方式,还是参考windows api的handle和linux api的fd。
即把ptr->function()改造为纯c的function(ptr)形式,这样还避免了虚函数的间接调用开销。
而需要运行时多态的虚函数,可以改为在ptr中显示保存函数指针来模拟虚表。
其实,各类大型c框架中的面向对象,就是这么实现的。
--------------------------
既然是动态库,也就是dll,那么mingw是可以链接的。
如果你确定dll里面的函数是stdcall
调用方式,而不是cdecl
,那么需要使用pexports工具生成一下模块定义文件(标准def文件)。
pexports.exe -v xxx.dll > xxx.def
然后使用mingw自带的dlltool
工具,利用上一步生成def
文件来创建.a
文件。
dlltool --dllname xxx.dll --def xxx.def --output-lib libxxx.dll.a
现在你可以通过libxxx.dll.a
文件来做链接了。
如果你的dll里面的函数是cdecl
方式调用的,那么就直接链接到dll
文件就是。
------------------------------
不同工具链生成的库原则上是不能互相静态连接的,别说mingw和vc,哪怕vc的不同版本间,很可能都不行。
跟调用规范没关系,两个编译器都支持多种调用规范。只要调用方看到的函数签名上的调用规范和实现方一致,编译器就能生成正确的代码。
如果abi兼容,可以考虑用动态调用的方式使用dll。
对于纯c函数和c++“接口”(只有纯虚函数,没有任何其他成员的类),大概率abi是兼容的。
以使用纯c函数为例:
1 定义函数指针
2 调用LoadLibrary加载dll
3 调用GetProcAddress获取函数地址
4 利用函数指针调用此函数
引自:https://dablelv.blog.csdn.net/article/details/88371267?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
C++可执行文件是由编译器将源代码编译成目标文件后链接生成的,那么如果目标文件由不同的编译器编译生成,能否相互链接呢?例如,能够将MSVC编译出来的目标文件和GCC编译出来的目标文件链接到一起,生成一个可执行文件吗?
对于上面这个问题,如果链接器可以将MSVC编译出来的目标文件和GCC编译出来的目标文件链接到一起,那么链接器首先需要支持MSVC编译生成的目标文件的格式PE/COFF和GCC的ELF格式。此外,不同格式的目标文件需要拥有相同的符号修饰标准、变量内存分布方式、函数调用方式等等。其中目标文件格式、符号修饰标准、变量内存分布方式、函数调用方式等这些跟二进制可执行代码兼容性相关的内容称为ABI(Application Binary Interface)。
ABI(Application Binary Interface)为应用程序二进制接口,描述了应用程序和操作系统之间,一个应用和它的库之间,或者应用的组成部分之间的二进制层面的底层接口。影响ABI的因素非常多,硬件、编程语言、编译器、链接器、操作系统等都会影响ABI。我们以C语言为例,来看编程语言是如何影响ABI的。对于C语言的目标代码来说,以下几个方面会决定目标文件之间是否二进制兼容:
(1)内置数据类型(如char、int、float等)的大小和在存储器中的存放方式(大端、小端、对齐方式等);
(2)组合数据类型(struct、union等)的存储方式和内存分布;
(3)外部符号和用户定义的符号之间的命名方式和解析方式。比如函数名func在C语言目标文件中是否会被解析成外部符号_func;
(4)函数调用方式,比如参数入栈顺序、返回值如何保存等;
(5)函数栈的分布方式,比如参数和局部变量在堆栈里的位置,参数传递方法等;
(6)寄存器使用约定,函数调用时哪些寄存器可以修改,哪些需要保存等。
以上只是部分因素会影响到ABI,其它的不再一一列举。到了C++,语言层面对ABI的影响又增加了许多额外的内容,可见,这些内容使得C++做到二进制兼容比C更加困难。增加的内容有:
(1)继承体系下的对象内存布局,比如基类、虚基类在继承类中的位置;
(2)指向类成员函数的指针(Pointer to Member)的内存分,如何通过指向成员函数的指针调用成员函数,如何传递this指针;
(3)如何调用虚函数,虚函数表的内容和分布形式,虚函数表指针在对象中的位置等;
(4)template如何实例化;
(5)外部符号的修饰;
(6)全局对象的构造和析构;
(7)异常的产生和捕获机制;
(8)标准库的细节问题、RTTI如何实现等;
(9)内联函数访问细节。
C++新增的内容包括但限于以上内容,对ABI的兼容带了极大的挑战,C++二进制兼容性不够友好也一直为人诟病。不仅不同编译器编译的二进制代码之间无法相互兼容,有时候连同一个编译器的不同版本之间的兼容性也不好。比如开发团队A开发了一个使用CompilerA版本的编译器编译的库libA.a,开发团队B开发了一个使用CompilerB版本的编译器编译的库libB.a,开发团队C想将库 libA.a 和库 libB.a 链接到程序ProgramC中,可能会出现不兼容的情况。
事实上C++ ABI尚未统一,目前的兼容情况仍不乐观,主要有两套体系,一是微软的Visual C++使用的标准,另一个是GNU的GCC所使用的标准,二者互不兼容。
API(Application Programming Interface)是应用程序编程接口,是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需了解源码和内部实现细节。比如,POSIX 标准,C99 标准等都是对API的规定。其中,POSIX 标准是不同操作系统间的API标准,POSIX 标准定义了类 Unix 操作系统应该为应用程序提供的接口。C99 标准则规定了C语言所需提供的库函数。
API是一种源代码级别的接口,同一个API,在不同平台(不同操作系统或硬件平台)的具体实现方式可能不同,但是接口的形式和功能必须一致,这样就能够保证使用统一的接口实现跨平台开发。如果API不兼容,那么开发出来的程序换一个环境将无法运行,即出现不兼容的情况。比如 POSIX 规定 printf() 原型必须统一,不同平台间的功能也需要是相同的,即向标准输出格式化输出用户指定的内容,这样就能保证应用程序在使用 printf() 后,在不同平台可以正确运行。
通过上面的介绍,可以知道 ABI 和 API 是两种完全不同的东西。二者的区别可总结概括为如下几点:
(1)描述的内容不同。ABI规定了二进制文件的格式、内容、装载/卸载程序的要求、函数调用时的参数传递规则、寄存器、堆栈的使用;API规定操作系统、硬件平台、服务组件、语言函数库等需要提供的功能函数接口;
(2)作用的层面不同。ABI描述二进制层面的接口,API描述代码级层面的接口;
(3)兼容的难度不同。ABI的兼容程度比API更为严格,即ABI实现兼容更加困难。