Как определить -stdlib = libС++ в препроцессоре?

Я думаю, что это часть проблемы в No type с именем "unique_ptr" в пространстве имен "std" при компиляции в LLVM/Clang. Согласно Marshall Clow, я могу обнаружить -stdlib=libc++ через _LIBCPP_VERSION:

Если вы пишете кросс-платформенный код, иногда вам нужно знать, какую стандартную библиотеку вы используете. Теоретически все они должны предлагать эквивалентную функциональность, но это просто теория. Иногда вам просто нужно знать. Лучший способ проверить libc++ - искать символ препроцессора _LIBCPP_VERSION. Если это определено, вы используете libc++.

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

К сожалению, это ломается с Apple Clang (3.4-SVN) и Clang (3.6), которые я построил из источников после загрузки из проекта LLVM. Я предполагаю, что тест действителен только в 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

Единственный эффект -stdlib=libc++ на препроцессоре - это изменить пути включения, которые он использует для поиска стандартных заголовков библиотек, поэтому вы не можете обнаружить наличие -stdlib=libc++ в командной строке как таковой, вы можете только определить, какие стандартные заголовки библиотек включены. Очевидно, вы не можете обнаружить, что без фактического включения одного или нескольких стандартных заголовков библиотек.

Если вы включаете любой заголовок libС++, тогда будет определен _LIBCPP_VERSION, поэтому способ обнаружения -stdlib=libc++ должен включать в себя хотя бы один заголовок библиотеки С++ и проверить _LIBCPP_VERSION.

Для libС++ рекомендуется #include <ciso646>, который не имеет никакой цели в С++ и ничего не объявляет, но для libС++ определяет макрос _LIBCPP_VERSION. Однако для libstdС++ исторически <ciso646> не определял макросов, таких как __GLIBCXX__, которые могут быть использованы для обнаружения libstdС++. Это изменилось с помощью GCC 6.1, поэтому <ciso646> можно использовать, но для более старых версий вам нужно включить другой заголовок для обнаружения libstdС++.