How to detect -stdlib=libc++ in the preprocessor? - c++

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.

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++.

Related

Determine gcc-toolchain version in clang++ preprocessor

According to cppreference, the gcc libstdc++ supports the parallelism TS. In layperson terms and for what's relevant for me that means #include <execution> works in g++ 9 and doesn't work in g++ 8 or before. In my source code I can handle this with
#if ( defined( __GNUC__ ) && __GNUC__ > 8 )
# define can_use_std_execution
# include <execution>
#endif
For my clang++ builds, the availability of <execution> depends on the --gcc-toolchain that I use. So instead of checking the __clang_major__, I'd like to check the gcc libstdc++ version in the preprocessor.
As far as I see in this compiler-explorer example, __GNUC__ is defined in clang but the compilation command is
-g -o /tmp/compiler-explorer-compiler120120-1672-4ffux6.smufm/output.s -mllvm --x86-asm-syntax=intel -S --gcc-toolchain=/opt/compiler-explorer/gcc-8.3.0 -fcolor-diagnostics -fno-crash-diagnostics /tmp/compiler-explorer-compiler120120-1672-4ffux6.smufm/example.cpp
i.e. the gcc toolchain is from gcc 8.3.0, but the value of __GNUC__ is 4.
What's a good way to query the gcc toolchain version in the preprocessor with clang? Ideally a way that checks the libstdc++ version in a way that's compatible for g++ and clang++ such that I don't have write a spaghetti if that checks the compiler first.
Grepping for ^#.*define.*9 in the compiler headers of gcc 9 it seems that
#include <bits/c++config.h>
#if _GLIBCXX_RELEASE > 8
# include <execution>
#endif
can do the job. From this conformance view this variable was introduced with the toolchain of gcc 7.

How to include C++ 17 headers with g++ 6.2.0 with -std=c++17 (optional, any, string_view, variant)

std::optional is in C++ 17, where it was std::experimental::optional before.
I tried compiling a file which included <optional>, with the command:
g++ -std=c++17 <filename>.cpp
(in the Bash terminal). I get the following error:
<filename>.cpp:5:20 fatal error: optional: No such file or directory
#include <optional>
^
compilation terminated
But I can #include <experimental/optional> just fine.
Am I missing some header files? How can I include the optional header?
I also can't include <any>, <string_view> or <variant>, getting the same error.
You can't.
GCC 6.2's support for C++17 is experimental, which is literally why the headers are arranged like this.
If only they'd done this for std::regex back in the day! It's a gift.
https://gcc.gnu.org/projects/cxx-status.html#cxx1z
I made a hacky workaround when faced with a situation like this:
#if defined(__GNUC__) && __GNUC__ < 7
# include <experimental/string_view>
# define string_view experimental::string_view
#else
# include <string_view>
#endif

Windows macros in clang 3.5, ndk r10e, CMake

I am building a CMake-based project for Android, using NDK r10e, x86_64, toolchain name is arm-linux-androideabi-clang3.5
Currently I'm getting very strange errors, suggesting that some Windows preprocessor macros come out, like _WIN32, _WIN64, __MINGW32__.
My project uses parts of stlplus3, version 3-03-11, particularly, filesystem
I use CMake Android toolchain from https://github.com/taka-no-me/android-cmake (this toolchain is used in OpenCV)
Here is CMake call
set ANDROID_SDK=C:\Android\sdk
set ANDROID_NDK=c:\Android\android-ndk-r10e
set ANDROID_EXECUTABLE=%ANDROID_SDK%\tools\android.bat
set CMAKE_PATH=C:\Program Files (x86)\CMake\bin
set PATH=%PATH%;%CMAKE_PATH%
cmake .. -DCMAKE_TOOLCHAIN_FILE="%~dp0\..\cmake\android.toolchain.cmake" -DCMAKE_BUILD_TYPE=Release -DANDROID_NDK=%ANDROID_NDK%^
-DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-clang3.5 -DANDROID_EXTRA_NDK_VERSIONS="-r10e"^
-DANDROID_ABI="armeabi-v7a with NEON"^
-G"Ninja"
Here is the compiler output:
[29/186] Building CXX object modules/3rdparty/stlplus3/CMakeFiles/stlplus3.dir/portability_fixes.cpp.o
FAILED: C:\Android\android-ndk-r10e\toolchains\llvm-3.5\prebuilt\windows-x86_64\bin\clang++.exe -DANDROID -D_CRT_SECURE_NO_WARNINGS -D__STDC_LIMIT_MACROS
-isystem C:/Android/android-ndk-r10e/platforms/android-8/arch-arm/usr/include -isystem C:/Android/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.8/include -isystem C:/Android/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -isystem C:/Android/android-ndk-r10e/sources/cxx-stl/gnu-libstdc++/4.8/include/backward
-IC:/opencv/android-clang/sdk/native/jni/include/opencv -IC:/opencv/android-clang/sdk/native/jni/include -I../modules/common
-fPIC -fPIE -flax-vector-conversions -Ofast -MMD -MT
modules/3rdparty/stlplus3/CMakeFiles/stlplus3.dir/portability_fixes.cpp.o -MF
modules/3rdparty/stlplus3/CMakeFiles/stlplus3.dir/portability_fixes.cpp.o.d -o modules/3rdparty/stlplus3/CMakeFiles/stlplus3.dir/portability_fixes.cpp.o
-c ../modules/3rdparty/stlplus3/portability_fixes.cpp
../modules/3rdparty/stlplus3/portability_fixes.cpp:12:10: fatal error: 'windows.h' file not found
#include "windows.h"
Here is the respective code from portability_fixes.hpp
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
#define MSWINDOWS
#endif
Here is the respective code from portability_fixes.cpp
#include "portability_fixes.hpp"
#ifdef MSWINDOWS
#include "windows.h"
#endif
I have also tried adding code like th following
#if defined(_WIN32) && defined(ANDROID)
#error win32
#endif
#if defined(_WIN64) && defined(ANDROID)
#error win64
#endif
#if defined(_WIN32_WCE) && defined(ANDROID)
#error win32_wce
#endif
And have got two error messages, "win32" and "win64".
I have also tried to work around these macros, adding ... && !defined(ANDROID). But then I've got an liker error about undefined symbol __mingw_aligned_malloc in file mm_malloc.h
Studying its code, I've found the following:
static __inline__ void *__attribute__((__always_inline__, __nodebug__,
__malloc__))
_mm_malloc(size_t __size, size_t __align)
....
void *__mallocedMemory;
#if defined(__MINGW32__)
__mallocedMemory = __mingw_aligned_malloc(__size, __align);
#elif defined(_WIN32)
__mallocedMemory = _aligned_malloc(__size, __align);
#elif defined(__ANDROID__)
__mallocedMemory = memalign(__align, __size);
#else
....
So, it looks like I'm doing something wrong, but don't understand what.
Switching compiler is not an option, because of compatibility issues with other projects.
So how do I make the compiler to undefine these macros?
Also, OpenCV builds fine with the same options.
Update: I've downloaded x86 version of NDK r10e and it has compiled my code fine.
Looks like bugs in 64-bit build of NDK r10e.
I've downloaded x86 version of NDK r10e and it has compiled my code fine.

__cplusplus < 201402L return true in gcc even when I specified -std=c++14

The directive:
#ifndef __cplusplus
#error C++ is required
#elif __cplusplus < 201402L
#error C++14 is required
#endif
The command-line: g++ -Wall -Wextra -std=c++14 -c -o header.o header.hpp
My g++ version: g++ (tdm-1) 4.9.2
The error C++14 is required is generated even when I added -std=c++14, I don't know why.
Please tell me how to fix this.
According to the GCC CPP manual (version 4.9.2 and 5.1.0):
__cplusplus This macro is defined when the C++ compiler is in use. You can use __cplusplus to test whether a header is compiled by a C compiler or a C++ compiler. This macro is similar to __STDC_VERSION__, in that it expands to a version number. Depending on the language standard selected, the value of the macro is 199711L, as mandated by the 1998 C++ standard; 201103L, per the 2011 C++ standard; an unspecified value strictly larger than 201103L for the experimental languages enabled by -std=c++1y and -std=gnu++1y.
You can check that g++ --std=c++14 defines __cplusplus as:
Version __cplusplus
4.8.3 201300L
4.9.2 201300L
5.1.0 201402L
For clang++ --std=c++14:
Version __cplusplus
3.3 201305L
3.4 201305L
3.5.x 201402L
3.6 201402L
3.7 201402L
So a safer check should probably be:
#ifndef __cplusplus
# error C++ is required
#elif __cplusplus <= 201103L
# error C++14 is required
#endif
As specified in the comment, this could mean partial C++14 support.
To check for a specific feature you could also try Boost Config (especially Macros that describe C++14 features not supported).

How to test the current version of GCC at compile time?

I would like to include a different file depending on the version of GCC. More precisely I want to write:
#if GCC_VERSION >= 4.2
# include <unordered_map>
# define EXT std
#elif GCC_VERSION >= 4
# include <tr1/unordered_map>
# define EXT std
#else
# include <ext/hash_map>
# define unordered_map __gnu_cxx::hash_map
# define EXT __gnu_cxx
#endif
I don't care about gcc before 3.2.
I am pretty sure there is a variable defined at preprocessing time for that, I just can't find it again.
There are a number of macros that should be defined for your needs:
__GNUC__ // major
__GNUC_MINOR__ // minor
__GNUC_PATCHLEVEL__ // patch
The version format is major.minor.patch, e.g. 4.0.2
The documentation for these can be found here.
Ok, after more searches, it one possible way of doing it is using __GNUC_PREREQ defined in features.h.
#ifdef __GNUC__
# include <features.h>
# if __GNUC_PREREQ(4,0)
// If gcc_version >= 4.0
# elif __GNUC_PREREQ(3,2)
// If gcc_version >= 3.2
# else
// Else
# endif
#else
// If not gcc
#endif
As a side note:
To find all the predefined macros:
Create empty file t.cpp
g++ -E -dM t.cpp