【发布时间】:2019-01-29 06:38:45
【问题描述】:
我遇到过这样一种情况,我可能想使用一个用一个版本的 gcc 编译的 C++ 共享对象库和一些将用另一个版本的 gcc 编译的代码。特别是,我想使用返回一些 STL 容器的方法,例如 std::string 和 std::map。
gcc website 和许多旧的 stackoverflow 帖子(例如 here)讨论了这个问题。我目前的理解是
关于这个问题的大部分关注和大多数帖子都是关于 .so 文件和 .dll 文件之间的交叉兼容性。由于编译器 ABI 不同,这非常困难。
对于使用不同版本的 gcc(至少 gcc 版本 >= 3.4)编译的 .so 文件之间的交叉兼容性,您只需要确保标准库 API 没有更改(并且,如果有,有dual ABI支持)。
我的问题与它在机器级别的工作方式有关。似乎 gcc 可以更改实现 std::string 的标头,即使库 API 没有更改,以提高效率或出于其他原因。如果是这样,那么两段不同的代码使用两个不同的std::string 标头进行编译,并且基本上定义了两个具有相同名称的不同类。我们如何保证,当我们将 std::string 从使用一个标头的代码传递到使用另一个标头的代码时,该对象不会以某种方式被破坏或误读?
例如,假设我有以下文件:
// File a.h:
#ifndef FILE_A
#define FILE_A
#include <string>
class X {
public:
std::string f();
};
#endif // FILE_A
// File a.cpp:
#include "a.h"
std::string X::f() {
return "hello world";
}
// File b.cpp:
#include <iostream>
#include <string>
#include "a.h"
int main() {
std::string x = X().f();
std::cout << x << std::endl;
}
(这里X 类的唯一目的是在我测试它的工作原理时,在共享对象库中引入更多的名称修饰。)
现在我编译如下:
/path/to/gcc/version_a/bin/g++ -fPIC -shared a.cpp -o liba.so
/path/to/gcc/version_b/bin/g++ -L. -la -o b b.cpp
当我执行b 时,b 有一个来自version_b 标头的std::string 定义。但是X().f() 生成的对象依赖于使用来自 gcc 的version_a 的标头副本编译的机器代码。
我不太了解编译器、链接器和机器指令的低级机制。但在我看来,我们在这里违反了一个基本规则,即每次使用类的定义都必须相同,如果不是,我们无法保证上述场景会起作用。
编辑:我认为解决我的困惑的主要方法是,“库 API”一词在这种情况下的含义比“API”一词的使用更普遍。我习惯于。 gcc 文档似乎以一种非常模糊的方式表明,对实现标准库的包含文件的几乎任何更改都可以被视为库 API 中的更改。有关详细信息,请参阅 cmets 中有关 Mohan 答案的讨论。
【问题讨论】:
-
“基本上定义了两个不同的同名类”为了保持兼容性,gcc 几乎不修改类,只是在它知道不会引起问题的方式。有些错误会保留数十年,因为修复它们会破坏 ABI。