我尝试使用 libmuparser 的 ubuntu 15.10 存储库版本(包 libmuparser2v5)。使用 gcc 编译可以正常工作,但不能使用 clang。我对此进行了更深入的研究,以提出以下最小(非)工作示例和一些问题。
考虑一个带有一个简单类的库,该类接受 astring
并返回 a 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
给出:
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
添加. 当一个方法返回一个. 这就是为什么 clang 只声称没有找到。get
set
string
Test::get()
我想这样做是为了给出get
一个调用签名,清楚地表明需要 c++11 才能与该函数链接。由于类型set
的原因,这不是必需__cxx11
的。
问题:
- 我的猜测是对的,还是可能有更多内容需要补充?
- 如果是这样,如何解决这个问题?
- 库编写者可以做些什么来使库可链接到 gcc 5.2 和 clang 3.6 编译代码?(源代码变化?)
- 库用户可以做什么来编译和链接 clang 3.6 与使用 gcc 5.2 编译的库,反之亦然。(源代码更改或编译标志?)
- 包维护者可以做些什么来使库可链接到 gcc 5.2 和 clang 3.6 编译代码?(编译标志?)
- 未来版本(哪个版本)中, clang 开发人员会修复这个问题吗?(默认情况下 ABI 兼容性?)但是现在有一个解决方案仍然很好,因为否则 clang 很难在 ubuntu 15.10 和衍生产品上使用。
附加信息:
- 的签名是在编译
get
时定义的。testlib.cpp
因此,共享对象不会产生任何影响。也可以nm -gC testlib-gcc.o
用来查看符号。 - 在一个用 clang 编译的库中,签名
get
是Test::get()
. 然后 gcc 找不到符号Test::get[abi:cxx11]()
,所以兼容性是相互破坏的。 - 使用 gcc 的编译标志
-D_GLIBCXX_USE_CXX11_ABI=0
(在此处找到)不是解决方案,从那时起 set 的签名也被更改:(Test::set(std::string)
但get
那时很好)。 - 在 clang 中使用编译标志
-stdlib=libc++
似乎没有帮助,因为它还报告了未定义的引用Test::get()
(以及更多未定义的引用)。