17

I think this is part of the problem at No type named 'unique_ptr' in namespace 'std' when compiling under LLVM/Clang. According to Marshall Clow, I can detect -stdlib=libc++ via _LIBCPP_VERSION:

If you're writing cross-platform code, sometimes you need to know what standard library you are using. In theory, they should all offer equivalent functionality, but that's just theory. Sometimes you just need to know. The best way to check for libc++ is to look for the preprocessor symbol _LIBCPP_VERSION. If that's defined, then you're using libc++.

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

Unfortunately, that breaks down with Apple's Clang (3.4-SVN) and the Clang (3.6) I built from sources after downloading from the LLVM project. I'm guessing the test is only valid under Xcode.

How can I reliably detect -stdlib=libc++ in the preprocessor?


Here is the test case:

$ 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;
}

This project does not use Autotools, Cmake, Boost, or other external libraries or frameworks.

Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • 1
    Xcode is not magic; are you sure this isn't already ready to go? It's possible that Xcode sets `_LIBCPP` on the commandline for you but it seems more likely to have been set by libc++ itself, no? – Lightness Races in Orbit Jul 27 '15 at 15:46
  • 1
    `_LIBCPP_VERSION` is the correct check, but you have to actually include a standard library header before it will be defined. – Jonathan Wakely Jul 27 '15 at 16:00
  • 1
    So perhaps you're checking the macro before including library headers? If library headers define the macro, that won't work. Can you show a specific minimal program and compiler invocation that demonstrates the problem? –  Jul 27 '15 at 16:00
  • 1
    Your test case is wrong, because you're not including a standard library header before trying to test which standard library implementation is in use. See my answer below. In any case this is probably not important because the question you say it's part of is based on another mistaken premise. – Jonathan Wakely Jul 27 '15 at 16:19

1 Answers1

28

The only effect -stdlib=libc++ has on the preprocessor is to change the include paths it uses to find standard library headers, so you can't detect the presence of -stdlib=libc++ on the command-line per se, you can only detect which standard library headers get included. Obviously you can't detect that without actually including one or more standard library headers.

If you include any libc++ header then _LIBCPP_VERSION will be defined, so the way to detect -stdlib=libc++ is to include at least one C++ library header and check for _LIBCPP_VERSION.

In C++20 and later, it is recommend to #include <version>, which was created specifically for this purpose. Prior to C++20, it is recommended to #include <ciso646> which serves no purpose in C++ and declares nothing, but for libc++ does define the _LIBCPP_VERSION macro. However, for libstdc++ historically <ciso646> did not define any macros such as __GLIBCXX__ that can be used to detect libstdc++. That changed with GCC 6.1 so <ciso646> can be used now, but for older releases you need to include a different header to detect libstdc++.

David Stone
  • 26,872
  • 14
  • 68
  • 84
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 1
    You should be able to include `` (which also has no effect) on pretty much any C++ implementation, even libstdc++, and it should work just as well for detecting `_LIBCPP_VERSION`, right? Or does that have its own list of implementations that didn't/don't support it? –  Jul 27 '15 at 17:40
  • 1
    @hvd, oops, I was confusing `` and ``. Including the latter for libstdc++ doesn't do anything, not even define our versioning macros, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65473 – Jonathan Wakely Jul 27 '15 at 17:57
  • 1
    Ah, thanks for the report link. If it does work for libc++ though, then it might be enough for the OP. –  Jul 27 '15 at 17:58
  • ciso646 *does* define "or", "and" and others. – dascandy Aug 31 '15 at 08:45
  • 1
    @dascandy, no, it shouldn't. In C++ those are keywords not macros, so they are pre-defined by the compiler. A footnote in the standard even says "177) In particular, including the standard header `` or `` has no effect." – Jonathan Wakely Aug 31 '15 at 13:07
  • 1
    For future visitors, on MS Visual Studio 2015 with [`_MSC_EXTENSIONS`](https://msdn.microsoft.com/en-us/library/b0084kay.aspx?f=255&MSPPError=-2147217396) set (the default) `` does create preprocessor definitions of those keywords. I haven't checked if this is still true in MS VS 2017. – TBBle Aug 06 '17 at 09:21
  • 3
    Note that C++20 will have [``](http://en.cppreference.com/w/cpp/header/version). – Mário Feroldi Jun 01 '18 at 00:41
  • 1
    And note that C++20 will also [remove ``](https://en.cppreference.com/w/cpp/header/ciso646), so to support that, some `__has_include` dance might need to happen (provided that libc++ will actually remove the header, of course). – Matthijs Kooijman Sep 23 '20 at 16:23
  • 1
    There's no way that libc++ will actually remove the header, because people will still compile with C++14 (say), and include it. – Marshall Clow Apr 03 '23 at 16:54