8

我尝试使用 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 只声称没有找到。getsetstringTest::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 编译的库中,签名getTest::get(). 然后 gcc 找不到符号Test::get[abi:cxx11](),所以兼容性是相互破坏的。
  • 使用 gcc 的编译标志-D_GLIBCXX_USE_CXX11_ABI=0(在此处找到)不是解决方案,从那时起 set 的签名也被更改:(Test::set(std::string)get那时很好)。
  • 在 clang 中使用编译标志-stdlib=libc++似乎没有帮助,因为它还报告了未定义的引用Test::get()(以及更多未定义的引用)。
4

0 回答 0