转自:http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system#OSXiOSandDarwin
OS identification macros are predefined by all C/C++ compilers to enable #if/#endif
sets to wrap OS-specific code. This is often necessary in cross-platform code that must use low-level library functions for fast disk I/O, inter-process communications, or threads. While differences between Windows and other OSes are acute, even differences among UNIX-style OSes can require #if/#endif
constructs.This article surveys common compilers and shows how to use predefined macros to detect common OSes at compile time.
See How to list compiler predefined macros for instructions on getting a list of macros for the compilers referenced here.
Throughout the following sections note:
Developer: | IBM |
Distributions: | AIX |
Processors: | POWER |
#if defined(_AIX) /* IBM AIX. ------------------------------------------------- */ #endif
Macro | GNU GCC/G++ | IBM XL C/C++ |
---|---|---|
_AIX |
yes | yes |
__unix |
yes | |
__unix__ |
yes | yes |
Notes:
Developer: | Open source |
Distributions: | DragonFly BSD, FreeBSD, OpenBSD, NetBSD |
Processors: | x86, x86-64, Itanium, POWER, SPARC, etc. |
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
#include <sys/param.h>
#if defined(BSD)
/* BSD (DragonFly BSD, FreeBSD, OpenBSD, NetBSD). ----------- */
#endif
#endif
Macro | Clang/LLVM | GNU GCC/G++ | |||||
---|---|---|---|---|---|---|---|
DragonFly BSD | FreeBSD | NetBSD | OpenBSD | FreeBSD | NetBSD | OpenBSD | |
unix |
yes | yes | yes | yes | |||
__unix |
yes | yes | yes | yes | |||
__unix__ |
yes | yes | yes | yes | yes | yes | yes |
__DragonFly__ |
yes | ||||||
__FreeBSD__ |
yes | yes | |||||
__NetBSD__ |
yes | yes | |||||
__OpenBSD__ |
yes | yes |
Notes:
__bsdi__
macro, but none of these distributions define it now. This leaves no generic "BSD" macro defined by the compiler itself, but all UNIX-style OSes provide a <sys/param.h>
file. On BSD distributions, and only on BSD distributions, this file defines a BSD
macro that's set to the OS version. Checking for this generic macro is more robust than looking for known BSD distributions with __DragonFly__
, __FreeBSD__
, __NetBSD__
, and __OpenBSD__
macros.BSD
macro within <sys/param.h>
. However, compilers for OSX, iOS, and Darwin do not define __unix__
. To detect all BSD OSes, including OSX, iOS, and Darwin, use an #if/#endif
that checks for __unix__
along with __APPLE__
and __MACH__
(see the later section on OSX and iOS).Developer: | Hewlett-Packard |
Distributions: | HP-UX |
Processors: | Itanium |
#if defined(__hpux) /* Hewlett-Packard HP-UX. ----------------------------------- */ #endif
Macro | GNU GCC/G++ | HP C/aC++ |
---|---|---|
hpux |
yes | |
__hpux |
yes | yes |
unix |
yes | |
__unix |
yes | yes |
__unix__ |
yes |
Developer: | Open source |
Distributions: | Centos, Debian, Fedora, OpenSUSE, RedHat, Ubuntu |
Processors: | x86, x86-64, POWER, etc. |
#if defined(__linux__) /* Linux. --------------------------------------------------- */ #endif
Macro | Clang/LLVM | GNU GCC/G++ |
Intel ICC/ICPC |
Oracle Solaris Studio |
Portland PGCC/PGCPP |
IBM XL C/C++ |
---|---|---|---|---|---|---|
linux |
yes | yes | yes | yes | yes | |
__linux |
yes | yes | yes | yes | yes | yes |
__linux__ |
yes | yes | yes | yes | yes | yes |
__gnu_linux |
yes | yes | yes | yes | ||
unix |
yes | yes | yes | yes | yes | |
__unix |
yes | yes | yes | yes | yes | yes |
__unix__ |
yes | yes | yes | yes | yes | yes |
Notes:
/proc/version
to get the distribution name and version, or invoke uname -a
from a Makefile
then set your own macro. However, writing Linux distribution-specific code is rarely necessary due to high compatibility between distributions._POSIX*
macros in <unistd.h>
. While Linux is compliant with the latest POSIX.1-2008 specification, Linux distributions erroneously set_POSIX_VERSION
to 200809L
, instead of leaving it set to 200112L
, as required by the POSIX specification. In practice, this is not a big issue since the existence, not value, of the _POSIX_VERSION
macro is sufficient to detect POSIX compliance. After that, the individual _POSIX_*
feature macros provide better information about which specific POSIX features are implemented (see later in this article for POSIX discussion).Developer: | Apple and open source |
Distributions: | OSX, iOS, Darwin |
Processors: | x86, x86-64, ARM |
#if defined(__APPLE__) && defined(__MACH__) /* Apple OSX and iOS (Darwin). ------------------------------ */ #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 /* OSX */ #endif #endif
Macro | Clang/LLVM | GNU GCC/G++ |
Intel ICC/ICPC |
Portland PGCC/PGCPP |
---|---|---|---|---|
__APPLE__ |
yes | yes | yes | yes |
__MACH__ |
yes | yes | yes | yes |
Macro | Clang/LLVM | GNU GCC/G++ |
---|---|---|
__APPLE__ |
yes | yes |
__MACH__ |
yes | yes |
Notes:
__APPLE__
and __MACH__
macros. The __MACH__
macro indicates the MACH kernel at the heart of OSX/iOS and partially derived from the obsoleteNeXTSTEP. For rigor, both of these macros must be defined to detect OSX/iOS. If only __MACH__
is defined, the OS is NeXTSTEP or one of the other OSes derived from the MACH kernel.__unix__
, __unix
, or unix
macros. They do define the BSD
macro in <sys/param.h>
(see laterdiscussion about BSD).__MACOSX__
. Some forum comments (like these) claim __OSX__
exists. These are incorrect. There are no such macros predefined by OSX compilers, but they may be defined by specific project Makefiles and platform-detector scripts like GNU autoconf
.macintosh
or Macintosh
macros. These were only available on the obsolete Mac OS 9 discontinued back in 2002.__APPLE__
macro is only defined by Apple's own compilers. This is incorrect. While it's true that compilers distributed by Apple define this macro, so do OSX distributions of Intel's ICC, the old IBM XL for PowerPC, and the latest direct downloads of Clang and GCC from open source web sites.<TargetConditionals.h>
in each platform's SDK provides TARGET_*
macros that indicate the OS. All of the macros exist for all platforms, but their values change between 0
and 1
flags as follows:
Mac OSX | iOS | iOS Simulator | |
---|---|---|---|
TARGET_OS_EMBEDDED |
0 |
1 |
0 |
TARGET_OS_IPHONE |
0 |
1 |
1 |
TARGET_OS_MAC |
1 |
1 |
1 |
TARGET_IPHONE_SIMULATOR |
0 |
0 |
1 |
TARGET_OS_MAC
is set to 1
for all platforms, and TARGET_OS_IPHONE
is 1
for iOS and the simulator. To detect OSX vs. iOS vs. the iOS simulator you have to check the macro values in a specific order (see below).Developer: | Oracle and open source |
Distributions: | Oracle Solaris, Open Indiana |
Processors: | x86, x86-64, SPARC |
#if defined(__sun) && defined(__SVR4) /* Solaris. ------------------------------------------------- */ #endif
Macro | Clang/LLVM | GNU GCC/G++ | Oracle Solaris Studio |
---|---|---|---|
sun |
yes | yes | yes |
__sun |
yes | yes | yes |
__sun__ |
yes | yes | |
__SunOS |
yes | ||
__svr4__ |
yes | yes | |
__SVR4 |
yes | yes | yes |
unix |
yes | yes | yes |
__unix |
yes | yes | yes |
__unix__ |
yes | yes |
Notes:
__sun
and sun
, despite Oracle's acquisition of Sun Microsystems in 2010. Clang and GCC also define __sun__
, but Solaris Studio does not.__sun
is not sufficient to identify Solaris. Compilers for the obsolete BSD-based SunOS also defined __sun
(and Solaris Studio still defines __SunOS
, even on System V-based Solaris). To identify Solaris specifically, the __sun
and __SVR4
macros must be defined. Also note that you need to check for upper-case __SVR4
instead of the lower-case __svr4
that's only defined by GCC and not by Solaris Studio.Developer: | Open source |
Distributions: | Cygwin |
Processors: | x86 |
#if defined(__CYGWIN__) && !defined(_WIN32)
/* Cygwin POSIX under Microsoft Windows. -------------------- */
#endif
Macro | Clang/LLVM | GNU GCC/G++ |
---|---|---|
__CYGWIN__ |
yes | yes |
__CYGWIN32__ |
yes | yes |
unix |
yes | yes |
__unix |
yes | yes |
__unix__ |
yes | yes |
Notes:
Cygwin provides a POSIX development environment for Windows, including shells, command-line tools, and compilers. Using Cygwin's libraries, POSIX applications can be built and run under Windows without any Windows-specific code.
__CYGWIN64__
. However, there is no 64-bit Cygwin, so this macro is never defined. It exists only in forum discussions about a possible future 64-bit Cygwin.__CYGWIN__
and the standard __unix__
macros are always defined by GCC, even when building a Windows application. For this reason, detecting POSIX builds under Cygwin must use an #if/#endif
that checks that __CYGWIN__
is defined, but _WIN32
is not.Developer: | Microsoft |
Distributions: | Windows XP, Vista, 7, 8 |
Processors: | x86, x86-64 |
#if defined(_WIN64) /* Microsoft Windows (64-bit). ------------------------------ */ #elif defined(_WIN32) /* Microsoft Windows (32-bit). ------------------------------ */ #endif
Macro | Clang/LLVM (Windows target) |
Clang/LLVM (MinGW target) |
GNU GCC/G++ (Windows target) |
GNU GCC/G++ (MinGW target) |
Intel ICC/ICPC |
Portland PGCC/PGCPP |
Microsoft Visual Studio |
||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
32-bit | 64-bit | 32-bit | 64-bit | 32-bit | 32-bit | 64-bit | 32-bit | 64-bit | 32-bit | 64-bit | 32-bit | 64-bit | |
__CYGWIN__ |
yes | ||||||||||||
__CYGWIN32__ |
yes | ||||||||||||
__MINGW32__ |
yes | yes | yes | yes | |||||||||
__MINGW64__ |
yes | yes | |||||||||||
unix |
yes | ||||||||||||
__unix |
yes | ||||||||||||
__unix__ |
yes | ||||||||||||
WIN32 |
yes | yes | yes | yes | |||||||||
_WIN32 |
yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes | yes |
__WIN32 |
yes | yes | yes | yes | yes | yes | |||||||
__WIN32__ |
yes | yes | yes | yes | yes | yes | |||||||
WIN64 |
yes | yes | |||||||||||
_WIN64 |
yes | yes | yes | yes | yes | yes | |||||||
__WIN64 |
yes | yes | yes | ||||||||||
__WIN64__ |
yes | yes | yes | ||||||||||
WINNT |
yes | yes | yes | yes | |||||||||
__WINNT |
yes | yes | yes | ||||||||||
__WINNT__ |
yes | yes | yes |
Notes:
-ccc-host-triple i386-pc-win32
") or the MinGW target (e.g. "-ccc-host-triple i386-pc-mingw32
"). The "-m32
" option builds 32-bit applications and "-m64
" builds 64-bit.-mwin32
" command-line option. While GCC is capable of building 64-bit applications, Cygwin is 32-bit only and the version of GCC included with it only builds 32-bit applications.__CYGWIN64__
. Since there is no 64-bit Cygwin, this macro is never defined. It exists only in forum discussions about a possible future 64-bit Cygwin.-mwin32
" command-line option. The "-m32
" and "-m64
" options build 32-bit and 64-bit applications._WIN32
and _WIN64
._MSC_VER
. The macro is defined with the compiler version number for Clang/LLVM, ICC, and Visual Studio, but it isn't defined by GCC or Portland Group compilers.__TOS_WIN__
for IBM's XL compiler on Windows (XL is still available for AIX and Linux), and __WINDOWS__
for the discontinued but open sourced Watcom compiler.POSIX and UNIX are not operating systems. Rather they are formal or de facto standards followed to some degree by all UNIX-style OSes.
Developer: | Standard |
Distributions: | All current UNIX-style OSes, including BSD, Linux, OSX, and Solaris |
Processors: | x86, x86-64, ARM, POWER, SPARC, etc. |
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
/* UNIX-style OS. ------------------------------------------- */
#include <unistd.h>
#if defined(_POSIX_VERSION)
/* POSIX compliant */
#endif
#endif
All UNIX-style OSes (see also UNIX below) have <unistd.h>
that defines macros indicating the level of POSIX compliance. The _POSIX_VERSION
macro value indicates the version of the standard with which the OS is compliant. Known values are:
198808L
for POSIX.1-1988199009L
for POSIX.1-1990199506L
for ISO POSIX.1-1996200112L
for ISO POSIX.1-2001The latest POSIX.1-2008 specification does not have a well-defined version number for the _POSIX_VERSION
macro. However, some Linux distributions erroneously assign _POSIX_VERSION
a value of200809L
instead of leaving it at 200112L
, as required by the POSIX specification.
From the POSIX specification, the preferred way to detect ISO POSIX.1-2008 compliance is with a run-time check using sysconf
.
if ( sysconf( _SC_VERSION ) >= 200809L ) { /* POSIX.1-2008 */ } else { /* Pre-POSIX.1-2008 */ }
While the #if/#endif
and sysconf
call above will both work to detect broad POSIX compliance, it's more useful to check for individual POSIX features flagged by macros in <unistd.h>
. The POSIX specification has a long list of these macros, but a few of the more useful ones include:
_POSIX_IPV6
indicates IPv6 address support._POSIX_MAPPED_FILES
indicates memory mapping support._POSIX_SEMAPHORES
indicates semaphore support for multi-threading._POSIX_THREADS
indicates pthreads support. A number of _POSIX_THREAD
* macros then indicate whether thread resource usage can be reported or thread scheduling priority controlled.Developer: | De facto standard |
Distributions: | All current UNIX-style OSes, including BSD, Linux, OSX, and Solaris |
Processors: | x86, x86-64, ARM, POWER, SPARC, etc. |
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) /* UNIX-style OS. ------------------------------------------- */ #endif
Macro | Cygwin (POSIX) | DragonFly BSD | FreeBSD | iOS | Linux | NetBSD | OpenBSD | OSX | Solaris |
---|---|---|---|---|---|---|---|---|---|
unix |
yes | yes | yes | yes | yes | yes | |||
__unix |
yes | yes | yes | yes | yes | yes | |||
__unix__ |
yes | yes | yes | yes | yes | yes | yes |
Macro | AIX | Cygwin (POSIX) | FreeBSD | iOS | HP-UX | Linux | NetBSD | OpenBSD | OSX | Solaris |
---|---|---|---|---|---|---|---|---|---|---|
unix |
yes | yes | yes | yes | yes | |||||
__unix |
yes | yes | yes | yes | yes | |||||
__unix__ |
yes | yes | yes | yes | yes | yes | yes | yes |
AIX | HP-UX | Linux | OSX | Solaris | |||||
---|---|---|---|---|---|---|---|---|---|
Macro | IBM XL C/C++ |
HP C/aC++ |
Intel ICC/ICPC |
Oracle Solaris Studio |
Portland PGCC/PGCPP |
IBM XL C/C++ |
Intel ICC/ICPC |
Portland PGCC/PGCPP |
Oracle Solaris Studio |
unix |
yes | yes | yes | yes | |||||
__unix |
yes | yes | yes | yes | yes | yes | yes | ||
__unix__ |
yes | yes | yes | yes | yes |
Notes:
#if/#endif
that checks multiple macros is required.#if/#endif
that checks __APPLE__
and __MACH__
is required (see the earlier section OSX and iOS).#if/#endif
that excludes _WIN32
is required to detect UNIX builds on Cygwin (see the earlier section on Windows).On UNIX-style OSes a common way to detect OS features is to use GNU's autoconf
. This tool builds configuration shell scripts that automatically check for OS and compiler features, build Makefiles, and set compiler flags. For code that only targets UNIX-style OSes, this works well. But autoconf
doesn't work for code that must compile on non-UNIX-style OSes (e.g. Windows) or within an IDE. And it's way overkill for many projects where a simple #if/#endif
set will do.
From the command line there are several ways to detect the OS. On UNIX-style OSes, the uname
command reports the OS name. On Windows, the ver
and winver
commands report the OS name. On Linux, the /proc/version
virtual file reports the Linux kernel version. But using any of these to automatically configure code requires scripts and Makefiles. And those have the same problems as autoconf
.
The most elegant solution is to eschew all detection scripts and simply use the above predefined macros already available on every OS and designed specifically for use in #if/#endif
sets for OS-specific code. Don't reinvent the wheel.
#if/#endif
sets for detecting common compilers.#if/#endif
sets for detecting desktop and server processors using compiler macros.__unix__
), and doesn't include discussion.