3

I'm on a project where I need to use libc++. I'm come up with the following problem:

When I try to compile the following code:

#include <string>
int main()
{
    std::string::size_type (std::string::*function)() const = &std::string::size;
    return 0;
}

I get the following error:

ld: symbol(s) not found for architecture x86_64

If I use the libstdc++ instead of libc++ I get no errors so the issue should to be related with libc++.

Full output below:

clang++ --stdlib=libc++ -v main.cpp 
Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.10.0 -emit-obj -mrelax-all -disable-free -disable-llvm-verifier -main-file-name main.cpp -mrelocation-model pic -pic-level 2 -mdisable-fp-elim -masm-verbose -munwind-tables -target-cpu core2 -target-linker-version 241.9 -v -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/6.0 --stdlib=libc++ -fdeprecated-macro -fdebug-compilation-dir /Users/filipe/Downloads -ferror-limit 19 -fmessage-length 197 -stack-protector 1 -mstackrealign -fblocks -fobjc-runtime=macosx-10.10.0 -fencode-extended-block-signature -fcxx-exceptions -fexceptions -fdiagnostics-show-option -fcolor-diagnostics -vectorize-slp -o /var/folders/8k/34ll5dcj3c5c9sph_bwk1zr00000gn/T/main-5b89bb.o -x c++ main.cpp
clang -cc1 version 6.0 based upon LLVM 3.5svn default target x86_64-apple-darwin14.1.0
ignoring nonexistent directory "/usr/include/c++/v1"
#include "..." search starts here:
#include <...> search starts here:
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1
 /usr/local/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/6.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /usr/include
 /System/Library/Frameworks (framework directory)
 /Library/Frameworks (framework directory)
End of search list.
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -dynamic -arch x86_64 -macosx_version_min 10.10.0 -o a.out /var/folders/8k/34ll5dcj3c5c9sph_bwk1zr00000gn/T/main-5b89bb.o -lc++ -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/6.0/lib/darwin/libclang_rt.osx.a
Undefined symbols for architecture x86_64:
  "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::size() const", referenced from:
      _main in main-5b89bb.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
4

2 回答 2

3

这看起来像libc++这个线程的一个错误:_LIBCPP_INLINE_VISIBILITY 和 std::string::length做类似的事情,霍华德·欣南特的回应是:

我相信这是由于外部模板和 __attribute__ ((__always_inline__)) 之间的编译器交互不佳。如果 std::string 未声明为 extern 模板,则编译器将概述 size() 成员,您将不会收到此链接错误。

在我看来,clang 不应该假设 extern 模板具有内联成员的定义,尤其是那些标记为 always_inline 的定义,而且它确实是一个 clang 错误,导致您看到的链接错误。

在 libc++ 中使用 always_inline 的基本原理是控制 libc++ 的 ABI。过去,我看到编译器在不同版本中使用不同的启发式方法来做出内联/大纲决策。这可能会导致代码被静默地添加到 dylib 中或从 dylib 中删除。通过使用 always_inline,我告诉编译器永远不要将该代码添加到 libc++.dylib 二进制文件中。

libc++ 定义和使用的每个宏都可以被覆盖。

_LIBCPP_INLINE_VISIBILITY 控制内联函数的属性,默认为:

#ifndef _LIBCPP_INLINE_VISIBILITY
#define _LIBCPP_INLINE_VISIBILITY __attribute__ ((__visibility__("hidden"), __always_inline__))
#endif

您可以通过以下方式关闭它:

-D_LIBCPP_INLINE_VISIBILITY=""

外部模板通过以下方式完成:

#ifndef _LIBCPP_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...) extern template __VA_ARGS__;
#endif

后一种更难“关闭”。咒语是:

-D'_LIBCPP_EXTERN_TEMPLATE(...)='

使用这些变通办法中的任何一种(或两种)都会使您的链接错误消失。但是针对 clang 的错误报告可能是一个更好的长期解决方案。

我无法在coliru上重现这一点,但我可以在wandbox上并使用使用-O2标志的优化使问题消失。我无法让 wandbox 接受-D上面建议的选项,所以不确定这是否有效。

在我的本地机器上,霍华德的解决方案有效:

clang++ -D_LIBCPP_INLINE_VISIBILITY="" -D'_LIBCPP_EXTERN_TEMPLATE(...)='

我还没有找到错误报告,如果我没有找到,提交一份可能是有意义的。

于 2015-03-29T02:09:48.097 回答
0

After looking into Shafik Yaghmour information, I found a solution that doesn't require modifying the compilation flags.

The solution is forcing the compiler to instantiate the extern template class. The final code is the following:

#include <string>

template class std::basic_string<char>;

int main()
{
    std::string::size_type (std::string::*function)() const = &std::string::size;
    return 0;
}

More information here: using extern template (C++11)

于 2015-03-29T13:07:38.730 回答