如何在预处理器中检测-stdlib=libc++?

17

我认为这是在编译LLVM/Clang时出现命名空间“std”中没有类型名称“unique_ptr”的一部分问题的原因。根据Marshall Clow的说法,我可以通过_LIBCPP_VERSION检测-stdlib=libc++

如果你在编写跨平台代码,有时需要知道你正在使用哪个标准库。理论上,它们应该都提供相同的功能,但这只是理论。有时您只需要知道。检查libc++的最佳方法是查找预处理器符号_LIBCPP_VERSION。如果定义了该符号,则说明您正在使用libc++。

#ifdef  _LIBCPP_VERSION
//  libc++ specific code here
#else
//  generic code here
#endif
很不幸,这在苹果的Clang(3.4-SVN)和我从LLVM项目下载并编译的源代码中构建的Clang(3.6)上都无法正常工作。我猜测这个测试只在Xcode下有效。
如何可靠地在预处理器中检测-stdlib=libc++
以下是测试案例:
$ cat test-clapple.cxx

// Need to test {C++03,C++11} x {libc++, no libc++}

// c++ -c test-clapple.cxx
//     - OK
// c++ -stdlib=libc++ -c test-clapple.cxx
//     - OK
// c++ -std=c++11 -c test-clapple.cxx
//     - FAILS, no type named 'unique_ptr' in namespace 'std'
// c++ -std=c++11 -stdlib=libc++ -c test-clapple.cxx
//     - OK

#include <ciso646>

#if (__cplusplus >= 201103L) || (_MSC_VER >= 1600)
# pragma message "C++11"
#elif (__cplusplus >= 199711L)
# pragma message "C++03"
#endif

#if (_LIBCPP_VERSION)
# pragma message "libc++"
#else
# pragma message "no libc++"
#endif

#if defined(__apple_build_version__)
# pragma message "Apple build"
#else
# pragma message "non-Apple build"
#endif

#if (__cplusplus >= 201103L) || (_MSC_VER >= 1600) // C++11
# include <memory>
#else
# include <tr1/memory>
#endif

// Manage auto_ptr warnings and deprecation in C++11
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1600)
  template<typename T>
    using auto_ptr = std::unique_ptr<T>;
#else
  using std::auto_ptr;
#endif // C++11

int main(int argc, char* argv[])
{
    return argc;
}

这个项目不使用Autotools、Cmake、Boost或其他外部库或框架。


1
Xcode并不是魔法;你确定这个已经准备好了吗?可能是Xcode为您设置了命令行上的_LIBCPP,但更有可能是由libc++自己设置的,不是吗? - Lightness Races in Orbit
1
_LIBCPP_VERSION 是正确的检查方式,但在其被定义之前,您必须实际上先包含一个标准库头文件。 - Jonathan Wakely
1
也许您是在包含库头文件之前检查宏?如果库头文件定义了该宏,那么这样做是行不通的。能否展示一个具体的最小程序和编译器调用,以演示问题? - user743382
1
你的测试用例有误,因为在尝试测试使用哪个标准库实现之前,你没有包含标准库头文件。请参见下面的答案。无论如何,这可能并不重要,因为你所说问题的基础是另一个错误的前提。 - Jonathan Wakely
1个回答

28
-stdlib=libc++ 对预处理器唯一的影响是改变了用于查找标准库头文件的包含路径,因此您不能单独检测命令行上是否存在-stdlib=libc++,您只能检测到包括哪些标准库头文件。显然,您无法在不实际包含一个或多个标准库头文件的情况下进行检测。
如果您包含任何libc ++头文件,则将定义_LIBCPP_VERSION,因此检测-stdlib=libc++的方法是至少包含一个C++库头文件并检查_LIBCPP_VERSION
在C++20及更高版本中,建议#include <version>,它专门为此而创建。在C++20之前,建议#include <ciso646>,它在C++中没有任何作用并且不声明任何内容,但对于libc ++可以定义_LIBCPP_VERSION宏。但是,对于libstdc ++,历史上<ciso646>没有定义任何宏,例如__GLIBCXX__,可用于检测libstdc ++。这在GCC 6.1之前发生了变化,因此现在可以使用<ciso646>,但对于较旧版本,需要包含不同的头文件才能检测libstdc ++。

1
你应该能够在几乎任何C++实现中包括<ciso646>(它也没有任何效果),甚至是libstdc++,这样就可以很好地检测 _LIBCPP_VERSION 了,对吧?或者它有自己的一些不支持的实现吗? - user743382
1
@hvd,糟糕,我混淆了<cuchar><ciso646>。即使为libstdc++包含后者也不会有任何作用,甚至不会定义我们的版本宏,请参见https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65473 - Jonathan Wakely
1
啊,感谢提供报告链接。如果它确实适用于libc++,那么对于OP来说可能已经足够了。 - user743382
1
@dascandy,不应该影响。在C++中,它们是关键字而不是宏,因此由编译器预定义。标准中的一个脚注甚至说:“177)特别地,包括标准头文件<iso646.h>或<ciso646>没有效果。” - Jonathan Wakely
3
请注意,C++20将拥有<version> - Mário Feroldi
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接