我尝试使用ubuntu 15.10存储库版本的libmuparser(包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
给出:
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> >)
因此,为cxx11
添加了ABI标记get
,但set
没有添加。当方法返回string
时,显然会添加标记。这就是为什么clang声称只是没有找到Test::get()
。
我想这样做是为了给get
一个调用签名,该签名表明需要c ++ 11来链接该函数。对于set
,由于类型中的__cxx11
,这不是必需的。
问题:
其他信息:
get
时定义testlib.cpp
的签名。因此共享对象不会有任何区别。也可以使用nm -gC testlib-gcc.o
查看符号。get
的签名为Test::get()
。然后gcc找不到符号Test::get[abi:cxx11]()
,因此兼容性相互破坏。-D_GLIBCXX_USE_CXX11_ABI=0
是没有解决方案,因为那时集的签名也发生了变化:Test::set(std::string)
(但是get
很好然后)。-stdlib=libc++
似乎没有帮助,因为它还报告了对Test::get()
的未定义引用(以及更多未定义的引用)。