To see a lot of reference, there's some differences of ideas about that:
Is it safety to use std::shared_ptr and std::unique_ptr at DLL boundary?
Pros:
Safe to use std::unique_ptr : https://www.codeproject.com/Articles/594671/How-to-ensure-proper-dynamic-library-boundary-cros
Another solution : https://isocpp.org/files/papers/n4028.pdf
Something about GCC: https://stackoverflow.com/questions/23874393/does-using-std-c11-break-binary-compatibility
Neutral / Cons:
Words with no reference? https://stackoverflow.com/questions/32088478/correctly-defining-dll-interfaces-with-c11-14
Suggestion about C++20 module proposal : https://softwareengineering.stackexchange.com/questions/176681/did-c11-address-concerns-passing-std-lib-objects-between-dynamic-shared-librar
The problem is
Since we package our code with dll/so, user may not use the same build environment and C-runtime-library, that we had to use the COMMON PART OF C/C++ with users.
For example, void*, char*, int, double, ...
with same platform, all other c-runtime-library or header will provide the same ASM code.
The first link show a picture to explain a case.
Why not STL?
A lot of STL were provided by template, so a part of STL code will build with user's code.
So the problem is affected by compiler and STL_template, we cannot know anything in different platforms.
The result
All binaries By GCC, it's safety to use STL in dll-boundary.
While the libstdc++ with GCC4.9 lack support of std::__cxx11
, it will cause link error when linking a high version of .so library.
REFs: https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
By using Clang++ to link a library built by MSVC, it's unsafe to deliver STL in dll boundary, because of not fully compatibly.
http://clang.llvm.org/docs/MSVCCompatibility.html
(: It's under construction...
By VS 2015 or later (MSVC v140 v141 v142), it's no problem on ABI compatibility with msvcSTL behavior.
https://docs.microsoft.com/zh-cn/cpp/porting/binary-compat-2015-2017?view=vs-2019
A method to prevent: Use my own smart_ptr in header file to keep same binary-code behavior.
How can we use C-style / C++03 Construction and destruction?
You may like private-implement idea to organize the code.
class MyInterface {}
class MyClass : MyInterface {}
MyInterface *createMyClass();
void *destoryMyClass(MyInterface*);
以及为什么会有ABI
假设我们写了一个.h文件,内有结构体struct A
1 Arugment: struct A编译出来的 汇编代码 受编译器影响
参见我之前的一个疑问:https://devblogs.microsoft.com/cppblog/optimizing-the-layout-of-empty-base-classes-in-vs2015-update-2-3/
同样的代码,在不同编译器下产生 不兼容的汇编代码
2 Example 不同版本STL库代码不兼容
我们有一个.so 返回了一个std::string,它在stack上开了20 bytes空间
正好我们用了个奇特编译器my,提供的头文件里string占 32 bytes空间
.so 返回给我们的代码一个stack上面的地址addr表示string,我们用完了释放它,运行的是my::string::free(addr)
然后我们退了 32 bytes 的栈,但是我们想要退20 bytes
按理说STL库不兼容,应该是源码上面写的问题,不是ABI的范畴吧?
对!这个其实应该叫STL-API不兼容!STL库是C++标准的一部分,在使用者视角就把他归类到编译器那堆儿了
正如上文所说library API + compiler ABI = library ABI
换句话说是 STL库的二进制文件不兼容(不能相互调用)
Example续
接上个例子,如果返回的是指针指向开在heap上的空间,那么会有“指针开在不同的heap管理器上”的问题,见https://blog.vrqq.org/archives/516/ 的MD MT部分,MT就是两个heap管理器。
3 Example 编译器ABI
C++虚类类有vtable虚表,那虚表是摆在内存里最前面还是摆在最后面呢?
不同的ABI规范着不同的编译器行为,比如我们可以自己约定一个ABI要求虚表在内存里摆在class结构体最后。
那么代码都一样,都遵循c++语法,但是ABI不一样,运行起来在内存里这个变量的布局就不一样,互相调用就摸不着门路!
疑问:C++ Name mangling能解决多少问题?
我觉得很好,起码有namespace解决了std::__cxx11这个后来有的namespace和之前的冲突,以及潜在隔离了不同编译器互相瞎调用(可能会使用不同ABI标准以及编译器自家STL库)
那我又有问题了,操作系统给提供了那么多库,我啥编译器写的程序不都能调用吗?
问得好,说明操作系统给提供的库兼容性非常棒!
而且比如我是python用户,想通过某些“中间工具”调用操作系统库,那么这个“中间工具”其实是现成的,并且是已经保证了ABI兼容性的。
解决方案2
用不用STL都可以,clang和gcc之间想相互调用,想象成其他语言(dolphi)和gcc相互调用,只需要他们使用的 返回值等等接口 在汇编代码上一致就可以。
但目前来看,clang向gcc兼容还under construction,有的可以调,有的不能掉。。
因为我们编译好的库(.so)是binary 二进制的,汇编代码。
所以我们其他语言调用,“STL”兼容,指的是编译出代码和binary汇编代码兼容
ABI 编译器toolset 和向前兼容
引自:https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
以gcc举例,尽量深入,不做其他编译器对比。。
- C++ Standard 现在有那么多比如 c++11 c++14 c++17,但是c++标准只规定了“语法”,以及STL库有什么,例如规定了std::vector要包含begin()函数,
begin()
返回iterator
,但是对于怎么实现没有要求 - STL库有很多家都有,比如gnu出的libstdc++,uClibc出的uclibc++,STL和c++ standard对应关系呢,可以是一个库兼容11 14 17,也可以为不同的c++ standard提供不同的分支,但是这都是做STL库的人的implementation,而C++标准并没有规定如何“实现”,只规定了语法,以及接口函数名。
- 也就是说,某公司出了个STL库,到底做成header-only还是咋,是他自愿,只要接口符合C++ standard就ok。扩展阅读 https://zhuanlan.zhihu.com/p/23016264
- GCC 是编译器,大名鼎鼎,老版本的gcc不能支持c++11语法,所以gcc的版本更新,对应着更多的语法支持,以及旧语法的更替。所以gcc有编译参数
-std=c++17
上面说到libstdc++.so 其实是和STL里面的.cpp编译而成的,是STL的具体实现。它和编译器gcc合起来叫Toolset
举例:#include <condition_variable> //这个库的接口是C++11标准规定的,这个头文件是GNU STL提供的 condituon_variable cv; cv.notify_one(); //这个notify_one()对应的代码就在libstdc++.so里面
使用这个命令一看究竟
strings /lib64/libstdc++.so.6 |grep condition_variable
接下来说ABI
对于开发者来说似乎没什么关系,我们来看下ABI规定了什么
https://itanium-cxx-abi.github.io/cxx-abi/abi.html
比如规定了name mangling的规则,规定了struct的内存布局,struct的大小,对齐方式,等等。主要针对的是,其他文件想link编译好的so文件时候需要按照什么规则。我们的程序 就按照上述规则把代码变成“兼容的”汇编代码。
所以ABI只是一个针对编译器的东西,但是为什么我们的库也要说ABI兼容呢?
因为ABI规定了例如命名、vtable问题,gcc编译器针对某ABI规则 把源代码转换成binary,那么我们就说 这个binary是遵守某ABI规则的,其他程序想link,也要遵守同样的ABI规则。
为什么微软的MSVC编译的.dll和llvm系列的有所谓的 ABI不兼容呢?
其实指的是有一部分不兼容,比如msvc2015之后约定了name mangling方法是加$符号,但是itanium-ABI约定是_Z。
那我们根本不能按照.h里面的函数声明,找到这个函数的implement。
再说libstdc++和GCC的版本关系
libstdc++也是从cpp源码编译的,也不是凭空从石头缝蹦出来。
编译就要用到编译器,所以就有了ABI兼容问题。那为什么ABI会更新呢?因为需求会变。。。
然而GNU家的STL实现,最近做的向前兼容就很好。比如用很多#define保证了向前兼容,一个libstdc++.so.6里面有很多小define打包了很多之前版本