Using predefined compiler macros in C and C++
Published on 2021-12-10. Modified on 2023-05-30.
C and C++ compilers provide a set of predefined macros which can be used to detect a wide variety of build settings and target properties at compile time. Combining them with the preprocessor allows for platform-specific code to be guarded at compile time.
Listing predefined macros
To list predefined compiler macros, we can use the following commands:
$ clang -dM -E -x c /dev/null
$ clang++ -dM -E -x c++ /dev/null
Options:
-dM
dumps a list of macros.-E
prints the result tostdout
instead of a file.-x c
and-x c++
select the source language when no file extension is available. If instead of/dev/null
we useddummy.{c,cpp}
, then these options would not be required.
Note: clang
(resp. clang++
) can be replaced by gcc
(resp. g++
).
Detecting the target operating system
We can used predefined compiler macros to detect the target operating system at compile time when working with C and C++ code. This can allow us to conditionally enable or disable parts of the code depending on the target, which is useful when writing cross-platform code.
The following code snippet detects common target operating systems:
#if defined(__linux__)
/* Linux ...........................................................*/
#elif defined(__APPLE__) || defined(__MACH__)
/* Mac OSX and Darwin (iOS) ........................................*/
#include <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR == 1
/* iOS in XCode simulator */
#elif TARGET_OS_IPHONE == 1
/* iOS on iPhone, iPad, etc. */
#elif TARGET_OS_MAC == 1
/* Mac OSX */
#endif
#elif defined(__CYGWIN__) && !defined(_WIN32)
/* POSIX Cygwin under Microsoft Windows ............................*/
#elif defined(_WIN32)
/* 32-bit Microsoft Windows ........................................*/
#elif defined(_WIN64)
/* 64-bit Microsoft Windows ........................................*/
#endif
Detecting the target processor architecture
In addition to operating system detection, predefined compiler macros can be used to determine the target architecture for which our code is compiled. In particular, we can detect x86 and AMD64 platforms using the following predefined macros:#if defined(__x86_64__) || defined(_M_X64)
/* AMD64 / x86-64 ..................................................*/
#elif defined(__i386) || defined(_M_IX86)
/* x86 .............................................................*/
#endif
Note: The _M_X64
and _M_IX86
variants are defined by the
Microsoft Visual Studio compiler.
Detecting the compiler name and version
We can also leverage predefined compiler macros to detect the compiler name and version of any major C and C++ compiler at compile time.
Compiler name
The following snippet checks for the three major C and C++ compilers currently used today.
#if defined(__clang__)
/* Clang/LLVM ......................................................*/
#elif defined(__GNUC__) || defined(__GNUG__)
/* GNU GCC/G++ .....................................................*/
#elif defined(_MSC_VER)
/* Microsoft Visual Studio .........................................*/
#endif
__GNUC__
and __GNUG__
were first
intended to identify the GNU compiler suite, Clang and some other compilers also define them to indicate their
compatibility with GNU behavior. To explicitly check for gcc
/g++
, use
#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__)
/* GNU GCC/G++ .....................................................*/
#endif
Compiler version
Compiler versions are encoded in a variety of ways that are not always compatible with one another.
- Clang/LLVM
__clang_major__
,__clang_minor__
and__clang_patchlevel__
expand to the major version, minor version and patch level of the compiler. For example, forclang 10.0.1
those macros would expand to10
,0
and1
respectively.__clang_version__
expands to a version string whose format is left unspecified and that can vary between different distributions.__GNUC__
,__GNUG__
,__GNUC_MINOR__
and__GNUC_PATCHLEVEL__
indicate with which version of the GNU compiler this particular version ofclang
is compatible.__VERSION__
expands to a long version string whose format is left unspecified and that can vary between different distributions. Contrary to__clang_version__
, this string also indicates GNU compatibility.
- GNU GCC/G++
__GNUC__
and__GNUG__
expand to the major version number, while__GNUC_MINOR__
and__GNUC_PATCHLEVEL__
contain the minor version number and patch level of the compiler respectively. For example, forgcc 10.2.0
those macros would expand to10
,2
and0
respectively. The__GNUC_PATCHLEVEL__
macro was introduced in version 3.0.__VERSION__
expands to a version string whose format is left unspecified and that can vary between distributions.
- Microsoft Visual Studio
_MSC_VER
expands to the major and minor version numbers as a single integer (e.g. 1500 for version 15.00)._MSC_FULL_VER
expands to the major version, minor version and build numbers as a single integer (e.g. 150020706 for version 15.00.20706). This macro was introduced in Visual Studio 2008._MSC_BUILD
expands to the revision number that appears after the major version, minor version and build numbers in the version identifier (e.g. 1 for version 15.00.20706.01). This macro was introduced in Visual Studio 2008.
See also
The following links point to resources that were once available online but are only accessible to the Internet Archive today. They list additional predefined macros for less common compilers and target platforms.
- Nadeau Software: How to list compiler predefined macros
- Nadeau Software: How to detect the operating system type using compiler predefined macros
- Nadeau Software: How to detect the processor type using compiler predefined macros
- Nadeau Software: How to detect the compiler name and version using compiler predefined macros