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:
-dMdumps a list of macros.-Eprints the result tostdoutinstead of a file.-x cand-x c++select the source language when no file extension is available. If instead of/dev/nullwe 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.1those macros would expand to10,0and1respectively.__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 ofclangis 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.0those macros would expand to10,2and0respectively. 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_VERexpands to the major and minor version numbers as a single integer (e.g. 1500 for version 15.00)._MSC_FULL_VERexpands 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_BUILDexpands 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