具有 C++ 接口的 C++ 动态库如何不破坏不同编译器版本之间的 ABI?
How can a C++ dynamic library with a C++ interface not break ABI between different compiler versions?
最近我一直在为我的爱好 3D 图形引擎项目使用 Assimp 库以加载 3D 模型。
该库以 DLL 的形式出现,伴随着一个导入库 (LIB) 和一个 EXP 文件(仅当从最新源构建时;我知道它包含有关导出函数的信息,类似于 LIB)
现在,让我问这个问题的惊喜部分如下:
我能够正确地使用两个不同版本库的 C++ 接口,没有任何 build/link 错误或 运行 次错误:
- 预构建的库,版本
3.1.1
,它是构建的并且依赖于较旧的 运行time(MSVCP110.DLL
,MSVCR110.DLL
)(我后来决定更改为我自己的构建,因为该二进制构建中存在链接错误)
- 我自己使用 VS 2013 编译器构建的源代码,取决于
MSVCP120.DLL
和 VCRUNTIME120.DLL
我能够将这两个库二进制文件与我的可执行二进制文件一起使用,当使用以下任一方式构建时:
- VS 2013 编译器
- VS 2015 编译器
令我惊讶的是,像上面描述的那样成功使用库,没有任何错误,原因是:
- 在互联网上阅读有关 C++ 库和接口如何在不同编译器版本之间不二进制兼容 (ABI) 的信息,因此不能与其他编译器构建的二进制文件一起使用。例外:通过 C 接口使用,保持 ABI 兼容性; COM 库(构建 COM 接口是为了克服同样的问题)。
- 使用不同编译器构建的二进制文件可能会引发问题,因为函数名称混淆(可以通过导入库解决?!)、不同版本的 allocators/destructors 完成的动态内存分配和释放。
任何人都可以提供一些关于此用例的见解,以及为什么我成功地将 VS 2013 构建的动态库与 VS 2015 工具集应用程序一起使用而没有上述任何问题?此外,在我的 3D 引擎应用程序的 VS 2013 构建和 VS 2015 构建中使用预构建二进制文件时,这同样适用。
它是一个孤立的案例还是 Assimp 库本身解决了问题并以确保兼容性的方式实现?
该库的实际使用非常简单:我使用了 Importer 对象 (class) 的本地堆栈变量,然后从那里操作和读取一堆结构中的信息;没有其他 classes(我认为)
只要 DLL 中使用的所有 classes 和内存都在 class 内部分配、释放和维护,并且您不跨接口边界使用标准库容器对象你这样做是完全安全的。
如果模块使用不同的编译器,甚至使用不同的 CRT,或者至少使用不同的 CRT,甚至不允许跨 DLL 边界使用 std:string。
事实上,使用纯接口(纯虚拟 classes)对于所有 VS C++ 编译器都是安全的。同样使用 extern "C" 是安全的。
因此,如果您交换的结构只是 PODs 或具有简单的纯数据对象,并且只要分配和销毁都在 DLL 内部完成,您就可以自由混合和使用由不同编译器构建的 DLL .
所以最好的例子就是windowsOS的DLL。他们使用简单数据结构的明确定义的接口。内存管理和分配(如 windows、菜单、GDI 对象)是通过透明句柄完成的。 DLL 对您来说只是黑盒。
希望我达到了所有要点。
最近我一直在为我的爱好 3D 图形引擎项目使用 Assimp 库以加载 3D 模型。
该库以 DLL 的形式出现,伴随着一个导入库 (LIB) 和一个 EXP 文件(仅当从最新源构建时;我知道它包含有关导出函数的信息,类似于 LIB)
现在,让我问这个问题的惊喜部分如下:
我能够正确地使用两个不同版本库的 C++ 接口,没有任何 build/link 错误或 运行 次错误:
- 预构建的库,版本
3.1.1
,它是构建的并且依赖于较旧的 运行time(MSVCP110.DLL
,MSVCR110.DLL
)(我后来决定更改为我自己的构建,因为该二进制构建中存在链接错误) - 我自己使用 VS 2013 编译器构建的源代码,取决于
MSVCP120.DLL
和VCRUNTIME120.DLL
我能够将这两个库二进制文件与我的可执行二进制文件一起使用,当使用以下任一方式构建时:
- VS 2013 编译器
- VS 2015 编译器
令我惊讶的是,像上面描述的那样成功使用库,没有任何错误,原因是:
- 在互联网上阅读有关 C++ 库和接口如何在不同编译器版本之间不二进制兼容 (ABI) 的信息,因此不能与其他编译器构建的二进制文件一起使用。例外:通过 C 接口使用,保持 ABI 兼容性; COM 库(构建 COM 接口是为了克服同样的问题)。
- 使用不同编译器构建的二进制文件可能会引发问题,因为函数名称混淆(可以通过导入库解决?!)、不同版本的 allocators/destructors 完成的动态内存分配和释放。
任何人都可以提供一些关于此用例的见解,以及为什么我成功地将 VS 2013 构建的动态库与 VS 2015 工具集应用程序一起使用而没有上述任何问题?此外,在我的 3D 引擎应用程序的 VS 2013 构建和 VS 2015 构建中使用预构建二进制文件时,这同样适用。 它是一个孤立的案例还是 Assimp 库本身解决了问题并以确保兼容性的方式实现?
该库的实际使用非常简单:我使用了 Importer 对象 (class) 的本地堆栈变量,然后从那里操作和读取一堆结构中的信息;没有其他 classes(我认为)
只要 DLL 中使用的所有 classes 和内存都在 class 内部分配、释放和维护,并且您不跨接口边界使用标准库容器对象你这样做是完全安全的。
如果模块使用不同的编译器,甚至使用不同的 CRT,或者至少使用不同的 CRT,甚至不允许跨 DLL 边界使用 std:string。
事实上,使用纯接口(纯虚拟 classes)对于所有 VS C++ 编译器都是安全的。同样使用 extern "C" 是安全的。
因此,如果您交换的结构只是 PODs 或具有简单的纯数据对象,并且只要分配和销毁都在 DLL 内部完成,您就可以自由混合和使用由不同编译器构建的 DLL .
所以最好的例子就是windowsOS的DLL。他们使用简单数据结构的明确定义的接口。内存管理和分配(如 windows、菜单、GDI 对象)是通过透明句柄完成的。 DLL 对您来说只是黑盒。
希望我达到了所有要点。