【问题标题】:Detect whether clang is compiling using c++11 or legacy libstdc++ standard library检测 clang 是使用 c++11 还是旧版 libstdc++ 标准库进行编译
【发布时间】:2014-04-27 04:42:48
【问题描述】:

我有一个 Xcode 项目,我正在迁移它以与 clang 的选项 -stdlib libc++ 一起使用,以启用 C++11 支持。我的一些源文件需要知道正在使用哪个库,例如,这样我才能执行以下操作:

#ifdef HAVE_CPP11_LIB_SUPPORT
  #include <memory>
#else 
  #include <tr1/memory>
#endif

#ifdef HAVE_CPP11_LIB_SUPPORT
  vector.emplace_back(newValue);
#else 
  vector.push_back(newValue);
#endif

我在查找为此选项设置的预处理器宏(如果确实有的话)时遇到了麻烦。我试过用以下方式转储 clang 的输出:

clang -x c++ -std=c++11 -stdlib=libc++ -dM -E - < /dev/null

比较:

clang -x c++ -std=c++11 -stdlib=libstdc++ -dM -E - < /dev/null

但这给出了相同的结果。请注意,我不想打开我们是否使用 c++11 language 设置,而是打开我们是否使用 c++11 library。有没有可靠的方法在代码中检测到这一点?

【问题讨论】:

    标签: xcode c++11 clang


    【解决方案1】:

    我不知道有什么方法可以保证便携,但这是我现在使用的方法:

    // libc++ detected:     _LIBCPP_VERSION
    // libstdc++ detected:  __GLIBCXX__
    #if defined(__clang__)
    #   if __has_include(<__config>) // defines _LIBCPP_VERSION
    #       include <__config>
    #   elif __has_include(<bits/c++config.h>) // defines __GLIBCXX__
    #       include <bits/c++config.h>
    #   else
    #       include <ios>
    #   endif
    #elif defined(__GNUC__) // gcc does not have __has_include
    #   include <ios> // ios should include the c++config.h which defines __GLIBCXX__
    #endif
    

    这不是很好,但现在对我有用。

    libc++ 定义了_LIBCPP_VERSION,stdc++ 定义了__GLIBCXX__,这很好,但不幸的是这些宏不是由编译器定义的。相反,它们是在非标准头文件中定义的,除非包含该头文件,否则您无法测试它们的定义。

    注意:显然 stdc++ 在旧版本中定义了 __GLIBCPP__。由于您需要 c++11,因此这不会成为问题。

    Clang(编辑:自 C++17 以来的标准)有一个很好的功能 __has_include 可用于测试这些,但如果没有找到任何标头,则宏回退到仅包括一个标准标题,希望在引擎盖下使用内部标题。我在这里有&lt;ios&gt;,但是要包含的标准标题的选择取决于您。您可以查找包含内部标头的标头(这适用于 linux 上的 gcc):

    grep -Rl '#include <bits/c++config.h>' /usr/include/c++
    

    选择您可能在项目中使用的任何标题。

    由于不能保证这适用于任何给定的过去或未来的编译器/标准库版本,我不会依赖这些定义来实现任何功能,但可选功能如下:

    #ifdef __GLIBCXX__
        std::set_terminate(__gnu_cxx::__verbose_terminate_handler);
    #endif
    

    【讨论】:

    • 啊哈,这是一系列相当复杂的检查。很好,但我认为这对我的要求来说可能有点过头了,所以我可能不得不找到一种更务实的方式来处理它
    • 我想您可以完全放弃“如果定义”的内容,只要您包含标准库中的标头,仍然可以检查给定的定义。我使用这个只是为了确定sure的定义非常宽松)。
    • 这个检查有很多问题,最大的问题是使用了仅供实现使用的内部标头。如今,GCC 确实支持__has_include,其他编译器也是如此。您可以通过 #ifdef __has_include 使您的检查面向未来,因此它可以与任何支持它的编译器一起使用。您也可以简单地包含一个轻量级标准头文件,例如 &lt;iosfwd&gt; 来检查 _LIBCPP_VERSION__GLIBCXX__ 宏,而不依赖于内部头文件。
    • @JonathanWakely 好点。我认为我的理由是我找不到保证_LIBCPP_VERSION 将包含在标准头文件中的文档,所以我更愿意找到定义它的文件。我想我为__GLIBCXX__ 做了同样的事情。我还相信,轻柔地依赖内部标题没有问题,因为 __has_include 会给我一个后备,以防它们在未来的版本中丢失。自从回答后我发现了 Boost Predef,我不会费心自己写这些检查。
    【解决方案2】:

    如果您正在编写此类检查,我建议您选择一个编译器/库版本并要求该版本或更新版本。半用 C++11 库特性是没有意义的。在 Mac OS X 上,只需要使用 clang++ -stdlib=libc++ 编译即可。

    【讨论】:

    • 在一个理想的世界里就是这样。就目前而言,我们正处于更大的 C++11 迁移的中间,考虑到遗留代码,这意味着它不是那么简单
    • 虽然我可以理解这可能是有原因的,但我看不出是什么阻止了您对所有内容都使用 libc++。它甚至可以在 C++03 模式下使用。
    • 问题是我们希望能够使用 C++11 语言特性(例如 auto、enum 类)但我们无法切换到 C++11 库。我正在进行迁移并希望确保我们可以使用 libc++ 或 libstdc++ 进行构建
    【解决方案3】:

    我使用下一个代码:

    #include <cstddef> // for __GLIBCXX__
    
    #ifdef __GLIBCXX__
    #  include <tr1/memory>
    #else
    #  include <memory>
    #endif
    

    source

    【讨论】:

      【解决方案4】:
      #ifdef __has_include
      # if __has_include(<ciso646>)
      #  include <ciso646>
      #  if defined(_LIBCPP_VERSION)
      #   define USING_LIBCPP 1
      #  endif
      # endif
      #endif
      
      #if !USING_LIBCPP
      # define USING_LIBSTDCXX 1
      #endif
      

      【讨论】:

        猜你喜欢
        • 2016-01-05
        • 2019-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-14
        • 1970-01-01
        • 2014-03-04
        • 1970-01-01
        相关资源
        最近更新 更多