我尝试使用 libmuparser 的 ubuntu 15.10 存储库版本(包 libmuparser2v5)。使用 gcc 编译工作正常,但使用 clang 则不行。我对此进行了更深入的研究,提出了以下最小(非)工作示例和一些问题。
考虑一个具有简单类的库,该类需要string
并返回一个string
.
testlib.h
:
#pragma once
#include <string>
struct Test {
std::string str;
void set(std::string s);
std::string get();
};
testlib.cpp
:
#include "testlib.h"
void Test::set(std::string s) {
str = s;
}
std::string Test::get() {
return str;
}
这是用 gcc 5.2.1 作为静态库编译的
g++ -Wall -c testlib.cpp -o testlib-gcc.o
ar rcs libtest-gcc.a testlib-gcc.o
现在正在编译一个应用程序main.cpp
#include <iostream>
#include "testlib.h"
int main()
{
Test p;
p.set("Hello!");
std::cout << p.get() << std::endl;
return 0;
}
与 clang 3.6.2-1 一起使用
clang++ main.cpp -o out-clang libtest-gcc.a
结果出现错误
main.cpp:(.text+0x79): undefined reference to `Test::get()'
现在我发现 gcc5 有一个新功能,称为 ABI 标记,可以帮助对库进行版本控制并在不兼容时防止链接。查看静态库
$ nm -gC libtest-gcc.a
gives:
testlib-gcc.o:
0000000000000026 T Test::get[abi:cxx11]()
0000000000000000 T Test::set(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
所以ABI标签cxx11
已添加用于get
但不是为了set
。当一个方法返回一个string
。这就是为什么 clang 只声称没有找到Test::get()
.
我想这样做是为了给予get
一个调用签名,明确表明需要 c++11 才能与该函数链接。为了set
这是没有必要的,因为__cxx11
在类型中。
问题:
- 我的猜测是否正确,或者还有什么需要补充的吗?
- If so, how to fix this?
- 什么可以图书馆作家如何使库可链接到 gcc 5.2 和 clang 3.6 编译代码? (源代码改变?)
- 什么可以图书馆用户使用 clang 3.6 对使用 gcc 5.2 编译的库进行编译和链接,反之亦然。 (源代码更改或编译标志?)
- 什么可以包维护者如何使库可链接到 gcc 5.2 和 clang 3.6 编译代码? (编译标志?)
- 这个问题会被修复吗铿锵开发者未来版本(哪个版本)? (默认情况下 ABI 兼容性?)但是现在如果有一个解决方案仍然很好,因为否则 clang 很难在 ubuntu 15.10 及其衍生产品上使用。
附加信息:
- 的签名
get
定义时testlib.cpp
已编译。因此,共享对象不会产生任何影响。也可以用一个nm -gC testlib-gcc.o
查看符号。
- 在用 clang 编译的库中,签名为
get
is Test::get()
。然后gcc找不到该符号Test::get[abi:cxx11]()
,因此兼容性被相互破坏。
- 使用编译标志
-D_GLIBCXX_USE_CXX11_ABI=0
对于海湾合作委员会(发现here http://developerblog.redhat.com/2015/02/05/gcc5-and-the-c11-abi/) 没有解决方案,从那时起 set 的签名也发生了变化:Test::set(std::string)
(but get
那么就可以了)。
- 使用编译标志
-stdlib=libc++
在 clang 中似乎没有帮助,因为它还报告了对的未定义引用Test::get()
(以及更多未定义的引用)。