How can I reliably detect the version of clang at preprocessing time? - c++

Apparently, the clang bundled with Xcode doesn't respect the upstream __clang_major__ and __clang_minor__ values, and instead reports an Xcode user-facing version of some sort.
Here are, for reference, the values for the various MacPorts installs of clang. They seem to respect the upstream release identifiers. I get similar values when testing on Linux.
➜ prohibit-clang-3.2 /opt/local/bin/clang++-mp-3.2 -dM -E -x c /dev/null |
grep __clang_m
#define __clang_major__ 3
#define __clang_minor__ 2
➜ prohibit-clang-3.2 /opt/local/bin/clang++-mp-3.3 -dM -E -x c /dev/null |
grep __clang_m
#define __clang_major__ 3
#define __clang_minor__ 3
➜ prohibit-clang-3.2 /opt/local/bin/clang++-mp-3.4 -dM -E -x c /dev/null |
grep __clang_m
#define __clang_major__ 3
#define __clang_minor__ 4
However, for some reason, the Apple provided clang has __clang_major__ and __clang_minor__ versions that track the Xcode version, not the base clang revision:
➜ prohibit-clang-3.2
/Applications/Xcode-4.6.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
-dM -E -x c /dev/null | grep __clang_m
#define __clang_major__ 4
#define __clang_minor__ 2
➜ prohibit-clang-3.2
/Applications/Xcode-4.6.3.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
--version
Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix
➜ prohibit-clang-3.2
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
-dM -E -x c /dev/null | grep __clang_m
#define __clang_major__ 5
#define __clang_minor__ 0
➜ prohibit-clang-3.2
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++
--version
Apple LLVM version 5.0 (clang-500.2.76) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin12.5.0
Thread model: posix
➜ prohibit-clang-3.2 /usr/bin/clang++ -dM -E -x c /dev/null | grep __clang_m
#define __clang_major__ 5
#define __clang_minor__ 0
This seems pretty awful, because it means you can't write conditional compilation lines like the following that will work accurately across both the Apple vendored clang, and clang built normally from the clang sources or from distro packages:
#if !defined(__clang__) || (__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ > 2))
// Thing that doesn't work for clang-3.2 due to an optimization bug.
#endif
I'd hate to need to extend that already pretty terrible preprocessor check to account for two different clang versioning schemes, one normal and one Apple. I'm not even sure how I could reliably detect that this is 'Xcode clang' rather than normal clang.
Does anyone have any suggestions on how to work around this? Is there a flag to pass to Apple's clang that will instruct it to report its 'true' version? Or has Apple doomed us to never be able to reliably use __clang_major__ and __clang_minor__? Are these not the macros I should be using here?

If the feature checking macros don't cover what you need to check for then you do need to treat vendor releases separately as they may have very different content from the open source LLVM releases. You can use __apple_build_version__ to check for a specific Apple LLVM version.

Related

clang 6 not supporting unordered_map::merge?

With this trivial example, I get a compilation error:
#include <unordered_map>
int main() {
std::unordered_map<int, int> a, b;
a.merge(b);
}
Error:
$ clang++ -std=c++17 merge.cpp
merge.cpp:5:4: error: no member named 'merge' in 'std::__1::unordered_map<int, int, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<std::__1::pair<const int, int> > >'
a.merge(b);
~ ^
1 error generated.
Versions:
$ clang++ --version
clang version 6.0.0 (tags/RELEASE_600/final)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin
According to cppreference, this should be legal since C++17. GCC 7 is happy to compile it.
I had same issue. I was outraged, why I can't compile application which use unordered_map::merge() using macOS 10.14.6
unordered_map::merge() was added in c++17 with proposal P0083R3 "Splicing Maps and Sets (Revision 5)", see github blame.
Documentation presents common compilers' support for new C++ features. Find row "Splicing Maps and Sets" and check you compiler version.
"Splicing Maps and Sets" compilers' support:
+-----------------------+---------------------+------+
| Compiler | Version | Link |
+-----------------------+---------------------+------+
| GCC libstdc++ | 7.1 | [1] |
| Clang libc++ | 8.0 | [2] |
| MSVC Standard Library | 19.12 (VS 2017 15.5)| [3] |
| Apple Clang | - | [4] |
+-----------------------+---------------------+------+
https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html
https://libcxx.llvm.org/Cxx1zStatus.html#cxx1z-status
https://learn.microsoft.com/cpp/overview/visual-cpp-language-conformance?view=vs-2019
Apple LLVM have special implementation of clang compiler, see description below
if you request a clang version, you get something like this:
➜ ~ clang++ --version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Apple's clang version number has nothing to with the official's one. Some users try to map apple's and official clang versions here. I think, it's not always possible.
Also I couldn't find exhaustive table of c++ standard support status for different Apple Clang versions, unlike another compilers (share it in comments if you have). All we have is official xcode release notes.
But mac OS users can still take advantage of all the new features of c++17. You should only... install original llvm. Detailed guide read in article "Installing LLVM/Clang on OSX" by Phillip Johnston.
Installing llvm using brew is much safer:
llvm is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another
version in parallel can cause all kinds of trouble.
Install llvm:
// optional "--with-toolchain" from article is deprecated
brew install llvm
Check status of llvm
➜ ~ brew info llvm
llvm: stable 8.0.1 (bottled), HEAD [keg-only]
Next-gen compiler infrastructure
https://llvm.org/
/usr/local/Cellar/llvm/8.0.1 (6,807 files, 3.3GB)
Poured from bottle on 2019-09-14 at 14:19:29
From: https://github.com/Homebrew/homebrew-core/blob/master/Formula/llvm.rb
==> Dependencies
Build: cmake ✔
Required: libffi ✔, swig ✔
==> Requirements
Build: xcode ✔
==> Options
--HEAD
Install HEAD version
==> Caveats
To use the bundled libc++ please add the following LDFLAGS:
LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib"
llvm is keg-only, which means it was not symlinked into /usr/local,
because macOS already provides this software and installing another version in
parallel can cause all kinds of trouble.
If you need to have llvm first in your PATH run:
echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshrc
For compilers to find llvm you may need to set:
export LDFLAGS="-L/usr/local/opt/llvm/lib"
export CPPFLAGS="-I/usr/local/opt/llvm/include"
Check clang version:
➜ ~ /usr/local/Cellar/llvm/8.0.1/bin/clang++ --version
clang version 8.0.1 (tags/RELEASE_801/final)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
Configure project
Now you can define compiler, I use CMake and add lines in CMakeLists.txt:
set(CMAKE_C_COMPILER "/usr/local/Cellar/llvm/8.0.1/bin/clang")
set(CMAKE_CXX_COMPILER "/usr/local/Cellar/llvm/8.0.1/bin/clang++")
set(CMAKE_CXX_STANDARD 17)
or pass cmake command's options:
➜ ~ cmake \
-D CMAKE_C_COMPILER="/usr/local/Cellar/llvm/8.0.1/bin/clang" \
-D CMAKE_CXX_COMPILER "/usr/local/Cellar/llvm/8.0.1/bin/clang++" \
/path/to/CMakeLists.txt
or define env variables:
➜ ~ export CC=/usr/local/Cellar/llvm/8.0.1/bin/clang
➜ ~ export CXX=/usr/local/Cellar/llvm/8.0.1/bin/clang++
➜ ~ cmake /path/to/CMakeLists.txt

warning: 'assume_aligned' attribute directive ignored

I just started with C++ and i think the best way is to look at source codes. I have code as follows in the header file.
#ifdef _MSC_VER
#define MYAPP_CACHE_ALIGNED_RETURN /* not supported */
#else
#define MYAPP_CACHE_ALIGNED_RETURN __attribute__((assume_aligned(64)))
#endif
I am using gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11) and its quite old. I get this warning during compilation:
warning: 'assume_aligned' attribute directiv e ignored [-Wattributes] –
How can I make the if statement more specific to fix the the warning during compilation?
It seems that assume_aligned is not supported in RHEL's GCC (it hasn't been backported to upstream gcc-4_8-branch and also not available in Ubuntu 14.04's GCC 4.8.4 so that wouldn't be surprising).
To emit a more user-friendly diagnostics you can do an explicit check for GCC version in one of your headers:
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9)
# warning "Your version of GCC does not support 'assume_aligned' attribute"
#endif
But this may not work if your distro vendor has back-ported assume_aligned from upstream (which is not the case for RedHat and Ubuntu but who knows about other distros). The most robust way to check this would be to do a build-time test in configure script or in Makefile:
CFLAGS += $(shell echo 'void* my_alloc1() __attribute__((assume_aligned(16)));' | gcc -x c - -c -o /dev/null -Werror && echo -DHAS_ASSUME_ALIGNED)
This will add HAS_ASSUME_ALIGNED to predefined macro if the attribute is supported by compiler.
Note that you can achieve similar effect with __builtin_assume_aligned function:
void foo() {
double *p = func_that_misses_assume_align();
p = __builtin_assume_aligned(p, 128);
...
}

Why is __ARM_FEATURE_CRC32 not being defined by the compiler?

I've been working on this issue for some time now, and I hope someone can point out my mistake. I guess I can no longer see the forest through the trees.
I have a LeMaker HiKey dev board I use for testing. Its AArch64, so its has NEON and the other cpu features like AES, SHA and CRC32:
$ cat /proc/cpuinfo
Processor : AArch64 Processor rev 3 (aarch64)
...
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
...
When I attempt to compile a program:
$ cat test.cxx
#if (defined(__ARM_NEON__) || defined(__ARM_NEON))
# define NEON_INTRINSICS_AVAILABLE 1
#else
# define NEON_INTRINSICS_AVAILABLE 0
#endif
#if BOOL_NEON_INTRINSICS_AVAILABLE
# include <arm_neon.h>
# if defined(__ARM_FEATURE_CRC32) || (__ARM_ACLE >= 200)
# include <arm_acle.h>
# endif
#endif
#include <stdint.h>
int main(int argc, char* argv[])
{
uint32_t crc = 0;
crc = __crc32b(crc, (uint8_t)0);
return 0
}
It results in the following:
$ g++ test.cxx -o test.exe
test.cxx: In function ‘int main(int, char**)’:
test.cxx:20:33: error: ‘__crc32b’ was not declared in this scope
crc = __crc32b(crc, (uint8_t)0);
^
test.cxx:22:1: error: expected ‘;’ before ‘}’ token
}
^
$ clang++ test.cxx -o test.exe
test.cxx:20:9: error: use of undeclared identifier '__crc32b'
crc = __crc32b(crc, (uint8_t)0);
^
test.cxx:21:11: error: expected ';' after return statement
return 0
^
;
2 errors generated.
A grep of the file system reveals arm_acle.h is in fact the header:
$ grep -IR '__crc32' /usr/lib
/usr/lib/gcc/.../include/arm_acle.h:__crc32b (uint32_t __a, uint8_t __b)
...
And according to ARM® C Language Extensions, Section 9.7 CRC32 Intrinsics, the missing symbols are suppose be present when __ARM_FEATURE_CRC32 is defined. Inspecting arm_acle.h confirms it.
For completeness, I tried compiling with -march=native, but the compiler rejected it.
Why is __ARM_FEATURE_CRC32 not being defined by the compiler?
What can I do to get the program to compile with the native features available on the board?
$ gcc --version
gcc (Debian/Linaro 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ clang --version
Debian clang version 3.5.0-10 (tags/RELEASE_350/final) (based on LLVM 3.5.0)
Target: aarch64-unknown-linux-gnu
Thread model: posix
$ g++ -dM -E - </dev/null | egrep -i '(arm|neon|acle)'
#define __ARM_NEON 1
$ clang++ -dM -E - </dev/null | egrep -i '(arm|neon|acle)'
#define __ARM_64BIT_STATE 1
#define __ARM_ACLE 200
#define __ARM_ALIGN_MAX_STACK_PWR 4
#define __ARM_ARCH 8
#define __ARM_ARCH_ISA_A64 1
#define __ARM_ARCH_PROFILE 'A'
#define __ARM_FEATURE_CLZ 1
#define __ARM_FEATURE_DIV 1
#define __ARM_FEATURE_FMA 1
#define __ARM_FEATURE_UNALIGNED 1
#define __ARM_FP 0xe
#define __ARM_FP16_FORMAT_IEEE 1
#define __ARM_FP_FENV_ROUNDING 1
#define __ARM_NEON 1
#define __ARM_NEON_FP 0xe
#define __ARM_PCS_AAPCS64 1
#define __ARM_SIZEOF_MINIMAL_ENUM 4
#define __ARM_SIZEOF_WCHAR_T 4
As for why this feature isn't enabled by default; this is an optional feature not present in the baseline ABI that your compiler targets, i.e. the binaries that your compiler produces are expected to be able to run on devices lacking the CRC feature.
At least for gcc, you can enable this feature with the -march modifier crc, like this:
$ gcc -dM -E - -march=armv8-a+crc < /dev/null | egrep -i '(arm|neon|acle|crc)'
#define __ARM_FEATURE_CRC32 1
#define __ARM_NEON 1
See https://gcc.gnu.org/onlinedocs/gcc-6.1.0/gcc/AArch64-Options.html (or the same page for older gcc versions) for more docs on how to set this.
I guess one could expect -march=native to do the same, but that option currently only seems to be implemented for x86 architectures.

Use CXX to query preprocessor defines?

I have a GNUmakefile that respects CXX and CXXFLAGS. It also performs some platform and architecture tests. Currently, the makefile assumes the host and target are the same:
IS_X86 = $(shell uname -m | $(EGREP) -c "i.86|x86|i86|amd64")
In an effort to improve robustness, I want to ask the tools what it is compiling for. I've come up with the following, but I'm not sure it is correct.
$ export CXX=clang++
$ export CXXFLAGS="-DNDEBUG -g2 -O3 -m32"
$ $CXX $CXXFLAGS -dM -E - < /dev/null | egrep "(i386|x86_64)"
#define __i386 1
#define __i386__ 1
#define i386 1
$ export CXX=clang++
$ export CXXFLAGS="-DNDEBUG -g2 -O3"
$ $CXX $CXXFLAGS -dM -E - < /dev/null | egrep "(i386|x86_64)"
#define __x86_64 1
#define __x86_64__ 1
My question is, will the above - with CXX and CXXFLAGS - work reliably to detect a target? Or do I need something else?
Here's the two reasons I ask. First, my experience with Autotools indicates something different. When Autotools performs a test like above, they test CPP, and sometimes CPP or CXX needs to include --isysroot (or other hacks) to get things configured properly.
Second, some toolchains, like Clang, integrate other components (like a preprocessor or assembler), so I can't use CPP directly under all circumstances.
In fact, doing something as simple as $CXX -Wa,-v - </dev/null (ask assembler for its version) results in an "unsupported option" error under Clang when using its integrated assembler. (Cf., With integrated assembler enabled, fail to fetch version string of assembler).
And just in case: this is not an Autools or Cmake project. It does not use Boost or any other libraries. Its a stand alone C++03 project.
My question is, will the above - with CXX and CXXFLAGS - work reliably to detect a target?
The answer is Yes, it will. The preprocessor or compielr driver (passing down to preprocessor) will mostly yield expected target defines with all else being equal. Notable exception is GCC and ARMv8/Aarch64, which is missing a slew of expected defines.
The thing to avoid is uname -m (and friends). Uname reports information on the host, and not the target.

GNU C++ how to check when -std=c++0x is in effect?

My system compiler (gcc42) works fine with the TR1 features that I want, but trying to support newer compiler versions other than the systems, trying to accessing TR1 headers an #error demanding the -std=c++0x option because of how it interfaces with library or some hub bub like that.
/usr/local/lib/gcc45/include/c++/bits/c++0x_warning.h:31:2: error: #error This file requires compiler and library support for the upcoming ISO C++ standard, C++0x. This support is currently experimental, and must be enabled with the -std=c++0x or -std=gnu++0x compiler options.
Having to supply an extra switch is no problem, to support GCC 4.4 and 4.5 under this system (FreeBSD), but obviously it changes the picture!
Using my system compiler (g++ 4.2 default dialect):
#include <tr1/foo>
using std::tr1::foo;
Using newer (4.5) versions of the compiler with -std=c++0x:
#include <foo>
using std::foo;
Is there anyway using the pre processor, that I can tell if g++ is running with C++0x features enabled?
Something like this is what I'm looking for:
#ifdef __CXX0X_MODE__
#endif
but I have not found anything in the manual or off the web.
At this rate, I'm starting to think that life would just be easier, to use Boost as a dependency, and not worry about a new language standard arriving before TR4... hehe.
There seems, with gcc 4.4.4, to be only one predefined macro hinting that -std=c++0x is in effect:
#define __GXX_EXPERIMENTAL_CXX0X__ 1
I don't have access to gcc 4.5.0 , but you can check that one yourself:
[16:13:41 0 ~] $ g++ -E -dM -std=c++0x -x c++ /dev/null >b
[16:13:44 0 ~] $ g++ -E -dM -std=c++98 -x c++ /dev/null >a
[16:13:50 0 ~] $ diff -u a b
--- a 2010-06-02 16:13:50.200787591 +0200
+++ b 2010-06-02 16:13:44.456912378 +0200
## -20,6 +20,7 ##
#define __linux 1
#define __DEC32_EPSILON__ 1E-6DF
#define __unix 1
+#define __GXX_EXPERIMENTAL_CXX0X__ 1
#define __LDBL_MAX_EXP__ 16384
#define __linux__ 1
#define __SCHAR_MAX__ 127
For one-line command do,
g++ -E -dM -std=c++98 -x c++ /dev/null > std1 && g++ -E -dM -std=c++0x -x c++ /dev/null > std2 && diff -u std1 std2 | grep '[+|-]^*#define' && rm std1 std2
gives you something like:
+#define __GXX_EXPERIMENTAL_CXX0X__ 1
If you compile with -std=c++0x, then __GXX_EXPERIMENTAL_CXX0X__ will be defined.
Well, from gcc-4.7 onwards you'll be able to check __cplusplus:
"G++ now sets the predefined macro __cplusplus to the correct value, 199711L for C++98/03, and 201103L for C++11"
This should be the correct, standard-compliant way to do it. Unfortunately, it doesn't work for most gcc installed in the wild.