【问题标题】:How to detect -stdlib=libc++ in the preprocessor?如何在预处理器中检测 -stdlib=libc++?
【发布时间】:2015-10-17 21:34:13
【问题描述】:

我认为这是No type named 'unique_ptr' in namespace 'std' when compiling under LLVM/Clang 问题的一部分。 According to Marshall Clow,我可以通过_LIBCPP_VERSION检测到-stdlib=libc++

如果您正在编写跨平台代码,有时您需要知道什么 您正在使用的标准库。理论上,他们都应该提供 等效功能,但这只是理论。有时你只是 需要知道。检查 libc++ 的最佳方法是查找 预处理器符号_LIBCPP_VERSION。如果已定义,那么您就是 使用 libc++。

#ifdef  _LIBCPP_VERSION
//  libc++ specific code here
#else
//  generic code here
#endif

不幸的是,这与 Apple 的 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 或其他外部库或框架。

【问题讨论】:

  • Xcode 并不神奇;你确定这还没有准备好去吗? Xcode 可能会在命令行上为您设置_LIBCPP,但它似乎更有可能是由 libc++ 本身设置的,不是吗?
  • _LIBCPP_VERSION 是正确的检查,但您必须在定义之前实际包含标准库头文件。
  • 所以也许你在包含库头文件之前检查了宏?如果库头定义了宏,那将不起作用。你能展示一个特定的最小程序和编译器调用来证明这个问题吗?
  • 您的测试用例是错误的,因为在尝试测试正在使用的标准库实现之前,您没有包含标准库头文件。请看下面我的回答。无论如何,这可能并不重要,因为您所说的问题是基于另一个错误的前提。

标签: c++ clang llvm c-preprocessor libc++


【解决方案1】:

-stdlib=libc++ 对预处理器的唯一影响是更改它用于查找标准库头文件的包含路径,因此您无法在命令行本身上检测到 -stdlib=libc++ 的存在,您只能检测包含了哪些标准库头文件。显然,如果不实际包含一个或多个标准库头文件,您将无法检测到这一点。

如果包含任何 libc++ 标头,则将定义 _LIBCPP_VERSION,因此检测 -stdlib=libc++ 的方法是包含至少一个 C++ 库标头并检查 _LIBCPP_VERSION

在 C++20 及更高版本中,建议使用专门为此目的创建的 #include &lt;version&gt;。在 C++20 之前,建议使用 #include &lt;ciso646&gt;,它在 C++ 中没有任何用途并且什么也不声明,但对于 libc++ 确实定义了 _LIBCPP_VERSION 宏。然而,对于 libstdc++ 历史上&lt;ciso646&gt; 没有没有定义任何可以用于检测 libstdc++ 的宏,例如 __GLIBCXX__。 GCC 6.1 改变了这种情况,所以现在可以使用 &lt;ciso646&gt;,但对于旧版本,您需要包含不同的标头来检测 libstdc++。

【讨论】:

  • 您应该能够在几乎任何 C++ 实现中包含 &lt;ciso646&gt;(这也没有影响),甚至是 libstdc++,它应该同样适用于检测 _LIBCPP_VERSION,对吧?还是有自己的不支持/不支持的实现列表?
  • @hvd,哎呀,我混淆了&lt;cuchar&gt;&lt;ciso646&gt;。为 libstdc++ 包含后者没有任何作用,甚至没有定义我们的版本控制宏,请参阅gcc.gnu.org/bugzilla/show_bug.cgi?id=65473
  • 啊,谢谢你的报告链接。如果它确实适用于 libc++,那么它对于 OP 来说可能就足够了。
  • @dascandy,不,不应该。在 C++ 中,这些是关键字而不是宏,因此它们是由编译器预定义的。标准中的一个脚注甚至说“177) 特别是,包括标准头 &lt;iso646.h&gt;&lt;ciso646&gt; 无效。”
  • 请注意,C++20 将有 &lt;version&gt;
猜你喜欢
  • 2010-11-14
  • 2016-09-11
  • 2013-07-03
  • 2016-11-14
  • 2015-12-15
  • 1970-01-01
  • 2013-05-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多