5

我知道以前有人问过这个问题,但是在你给我一个减号并报告重复的问题之前,请思考一下:

在所有先前的答案中,每个人都说对象内存布局取决于编译器。那么,共享库(*.dll、*.so)如何能够导出和导入 c++ 类,并且即使来自不同的编译器也绝对可以组合起来?考虑在 mingw 下编写的 DirectX 应用程序。DirectX 是使用 MSVC++ 编译的,那么这些环境如何在内存布局上达成一致?我知道 DirectX 严重依赖 C++ 类和多态性。

换个方式问:假设我有一个选择的架构(例如 Windows、intel x86)并且我正在尝试编写一个新的编译器。我如何知道如何访问由另一个编译器编译的 .dll lib 提供的类实例(vtable、成员字段)?还是只是这样:M$ 编写了 VC++,从那时起它就成为了不成文的标准,而所有其他编译器都“出于兼容性原因”这样做?那么 linux 或其他操作系统呢?

编辑:

好吧,我承认,DirectX 的例子很糟糕,因为 COM 规范......

另一个例子:QT。我将 QT 与 mingw 一起使用,但我知道也有可用的 MSVC 版本。我不知道差异是否仅在标头中,或者共享库(dll-s)是否也不同。如果是,这是否意味着我必须分发包含 qt 库的应用程序,所以如果有人碰巧有不同编译器的库,它不会混淆?(很好的内存和代码共享,对吧?)。或者它们是否相同,并且无论如何都有一些不成文的法律?

编辑2:

我安装了一个不同的 qt 版本(msvc 2010)只是为了看看什么是共享的和不共享的。似乎共享(他们真的共享)库是不同的。似乎我真的必须为我的应用程序提供 qt-libs ......这不是一件小事(例如 QtGui 8-9MB)。那么其他较小的库呢,其作者不太愿意为其他编译器提供版本?这是否意味着我被他们原来的编译器困住了?如果我想使用由不同编译器编译的两个不同库怎么办?

4

2 回答 2

9

基本上你在问一个ABI。

在某些情况下(例如,在 Itanium 上)有一个指定 ABI 的文档,基本上每个人都遵循该文档。

在 DirectX 的情况下,它有点相同:微软已经发布了 COM 规范,因此任何遵循这些规范的人都可以与基本上任何 COM 对象进行互操作(在合理范围内——64 位编译器可能无法工作为 Windows 3.1 编写的 16 位 COM 对象)。

对于大多数其他事情,您或多或少是靠自己来解决问题的。通常至少有一点以文档的方式发布,但至少根据我的经验,它经常略过一些最终很重要的细节,没有完全可靠地更新,在某些情况下完全是错误的。在大多数情况下,它的组织也很糟糕,因此您几乎可以从任何可以找到的地方收集您可以找到的东西,并且当您用完信息(或耐心)时,请进行一些逆向工程来填补缺失的部分。

编辑:对于像 Qt 库这样的非 COM 的东西,你有几个选择。一种是静态链接到 Qt 库,因此您需要的 Qt 代码直接链接到您的可执行文件中。如果你想使用一个 DLL,那么是的,你几乎被困在你的应用程序中分发一个 DLL,它将特定于你用来生成应用程序本身的编译器。不同的编译器(甚至不同版本或具有相同编译器的一组编译标志)通常需要不同的 DLL(可能是相同的源代码,但为了适应编译器的变化而构建)。

这也有例外,但它们与我上面概述的非常相似。例如,用于 Windows 的英特尔编译器通常使用 Microsoft 的标准库,并且也可以使用为 Microsoft 编译器构建的大多数(如果不是全部)其他库。这很大程度上是因为英特尔已经竭尽全力确保他们的编译器使用与微软相同的调用约定、名称修改方案等。它之所以奏效,是因为他们付出了很多努力使其发挥作用,而不是因为任何不成文的法律或类似的东西。

于 2012-05-25T23:04:39.180 回答
3

DirectX 是一种基于 COM 的技术,这就是为什么它可以在 C 中与各种编译器一起使用。引擎盖下的 COM 接口是一个模拟 VMT 的类 C 结构。

从技术上讲,DirectX 有一个中间自动生成的 C++ 接口,但是关于可以跨不同编译器使用以 .dll 导出的类的一般断言是错误的。

编辑1:

不幸的是,使用 MSVC 构建的 QT dll 与 gcc 构建的 dll 不兼容,这是由于自然原因:不同的 C++ 编译器 - 不同的 ABI 和不同的运行时库(mingw 使用较旧的 MSVCRT,这就是为什么 pure-C . dll 可以在 MSVC 中使用,反之亦然)。一些编译器偶然或部分有意地匹配它们的 ABI,但 MSVC/gcc 绝对不是这种情况。QT 也可以构建为静态库,因此要重新分发内容,可能只是静态链接。

DLL 中 C++ 类的名称修改很大程度上取决于所使用的编译器前端。许多知名公司的商业编译器使用 EDG 的 C++ 解析器,这就是为什么类名和重载函数具有相似或匹配的签名。

编辑2:

“如果我想使用由不同编译器编译的两个不同库怎么办?”

如果您迫切需要两个库的某些特定功能(我的意思是一些具体操作,而不是整个框架),那么没有库的源代码的方法是编写一些包装器并将此包装器编译为 C 风格的 .dll .

可以将其想象为拥有“两种不同的 C++-es,C++-1 和 C++-2”。ABI/Runtime 的问题与使用 C 中的 Pascal 代码或链接一些较旧的 Fortran 库没有什么不同。

于 2012-05-25T23:00:57.370 回答