In modern C++ cross-platform development, developers often need to write conditional compilation code based on compiler type, compiler version and target operating system. This capability is crucial for compatibility handling, performance optimization, feature enabling (such as C++11/14/17/20), and calling platform-specific APIs.
This article will systematically explain how to accurately determine the compiler type, version, and operating system in C++ source code and Qt .pro project files, providing a wealth of directly reusable code examples.
1. Why is it necessary to detect the compiler and operating system?
- API Differences: Windows uses
<span>CreateFile</span>, while Linux uses<span>open()</span>. - Compiler Extensions: MSVC supports
<span>__declspec</span>, GCC supports<span>__attribute__</span>. - Standard Support Differences: Older versions of GCC do not support C++17’s
<span>std::filesystem</span>. - ABI Compatibility: Binary interfaces generated by different compilers are incompatible.
- Debugging and Logging: Printing compilation environment information in logs facilitates problem tracking.
2. Compiler Type and Version Detection (C++ Preprocessor Macros)
2.1 Microsoft Visual C++ (MSVC)
MSVC exposes its internal version number through the <span>_MSC_VER</span> macro. Note: Visual Studio version ≠ MSVC version.
Common Judgment Template:
#ifdef _MSC_VER
# pragma message("Detected MSVC compiler")
#if _MSC_VER >= 1900
// Visual Studio 2015 and above, supports most C++11/14
#define HAS_MODERN_CPP1
#endif
#if _MSC_VER >= 1930
// Visual Studio 2022 (v17.0)
#define IS_VS20221
#endif
#endif
Complete Correspondence Table (Key Nodes):
<span>_MSC_VER</span> |
Visual Studio Version | Year | C++ Standard Support |
|---|---|---|---|
| 1200 | VS 6.0 | 1998 | C++98 |
| 1300 | VS .NET 2002 | 2002 | |
| 1400 | VS 2005 | 2005 | |
| 1500 | VS 2008 | 2008 | |
| 1600 | VS 2010 | 2010 | C++03 + Some C++11 |
| 1700 | VS 2012 | 2012 | C++11 |
| 1800 | VS 2013 | 2013 | Complete C++11 |
| 1900 | VS 2015 | 2015 | C++14 |
| 1910–1916 | VS 2017 | 2017 | C++17 |
| 1920–1929 | VS 2019 | 2019 | C++17/20 |
| 1930+ | VS 2022 | 2021 | C++20/23 |
💡 Tip: You can obtain a more precise build number through
<span>_MSC_FULL_VER</span><span> (e.g., 193231328).</span>
2.2 GCC (GNU Compiler Collection)
GCC uses <span>__GNUC__</span>, <span>__GNUC_MINOR__</span>, and <span>__GNUC_PATCHLEVEL__</span> to represent its version.
#ifdef __GNUC__
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
// GCC 4.8+ supports most features of C++11
#define COMPILER_HAS_CXX111
#endif
#if __GNUC__ >= 7
// GCC 7+ defaults to C++14
using byte = std::byte; // C++17
#endif
#endif
Print GCC Version (for debugging):
#ifdef __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 100200
// GCC 10.2.0
#endif
#endif
2.3 Clang
Clang is compatible with GCC macros but has its own <span>__clang__</span> and <span>__clang_major__</span>.
#ifdef __clang__
#if __clang_major__ >= 10
// Clang 10+ has good support for C++20
#endif
// Note: Clang is often misidentified as Apple GCC on macOS
#ifdef __apple_build_version__
// Apple Clang (non-open-source LLVM Clang)
#endif
#endif
2.4 MinGW / MinGW-w64
MinGW is a port of GCC for Windows, which can be identified using <span>__MINGW32__</span> or <span>__MINGW64__</span>.
#ifdef defined(__MINGW32__) || defined(__MINGW64__)
#define IS_MINGW1
// May require special link libraries (e.g., -lws2_32)
#endif
2.5 Other Compilers
#ifdef __BORLANDC__
// Old Borland C++ Builder
#endif
#ifdef __CYGWIN__
// Cygwin environment (POSIX on Windows)
#endif
#ifdef __INTEL_COMPILER
// Intel C++ Compiler
#endif
3. Operating System Detection
3.1 Windows Series
#ifdef _WIN32
// All Windows (including 64-bit)
#ifdef _WIN64
// 64-bit Windows
#else
// 32-bit Windows
#endif
// Distinguish program types
#ifdef _WINDOWS
// GUI application (WinMain entry)
#endif
#ifdef _CONSOLE
// Console application (main entry)
#endif
#endif
Windows Version Control (via <span>WINVER</span> and <span>_WIN32_WINNT</span>)
These macros are used to control the APIs available in the Windows SDK.
// Define before including windows.h
#define WINVER 0x0601 // Windows 7
#define _WIN32_WINNT 0x0601
#include <windows.h>
#if (_WIN32_WINNT >= 0x0600)
// Safe to use Vista+ APIs, such as SetThreadDescription
HRESULT hr = SetThreadDescription(GetCurrentThread(), L"MyThread");
#endif
⚠️ Note: If not explicitly defined, the SDK will use a default value (which may be outdated), causing new APIs to be unavailable.
3.2 Unix/Linux/macOS
#ifdef defined(__linux__)
#define OS_LINUX1
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_MAC
#define OS_MACOS1
#elif TARGET_OS_IPHONE
#define OS_IOS1
#endif
#elif defined(__FreeBSD__)
#define OS_FREEBSD1
#endif
4. Determining Compiler in Qt .pro Files (qmake)
Qt’s <span>.pro</span> files use qmake syntax, not C++ preprocessor.
4.1 Determine MSVC Version
# Check if it is MSVC
win32-msvc {
message("Building with MSVC")
# Check MSVC version number (Note: MSC_VER is a string in qmake!)
greaterThan(MSC_VER, 1900) {
message("MSVC > 2015 (VS2017+)")
DEFINES += MODERN_MSVC
}
equals(MSC_VER, 1930) {
message("Detected Visual Studio 2022")
}
}
🔍 Note:
<span>MSC_VER</span>is automatically set by Qt in qmake, and its value is a string (e.g., “1932”), so use<span>greaterThan</span>instead of numerical comparison.
4.2 Determine GCC/Clang
unix {
contains(QMAKE_CC, "gcc") {
message("Using GCC")
QMAKE_CXXFLAGS += -Wall -Wextra
} else: contains(QMAKE_CC, "clang") {
message("Using Clang")
QMAKE_CXXFLAGS += -Weverything
}
}
4.3 Operating System Detection
win32 {
LIBS += -lws2_32 -luser32
}
linux {
LIBS += -ldl -lpthread
}
macx {
QMAKE_LFLAGS += -framework CoreFoundation
}
5. Practical Example: Writing a Cross-Platform Compatible Header File
Create <span>platform_detect.h</span>:
#ifndef PLATFORM_DETECT_H
#define PLATFORM_DETECT_H
// === Compiler Detection ===
#ifdef defined(_MSC_VER)
#define COMPILER_MSVC1
#define COMPILER_NAME "MSVC"
#define COMPILER_VERSION _MSC_VER
#elif defined(__clang__)
#define COMPILER_CLANG1
#define COMPILER_NAME "Clang"
#define COMPILER_VERSION (__clang_major__ * 100 + __clang_minor__)
#elif defined(__GNUC__)
#define COMPILER_GCC1
#define COMPILER_NAME "GCC"
#define COMPILER_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
#define COMPILER_UNKNOWN1
#define COMPILER_NAME "Unknown"
#endif
// === Operating System Detection ===
#ifdef defined(_WIN32)
#define OS_WINDOWS1
#ifdef defined(_WIN64)
#define OS_ARCH "x64"
#else
#define OS_ARCH "x86"
#endif
#elif defined(__linux__)
#define OS_LINUX1
#define OS_ARCH (__SIZEOF_POINTER__ == 8 ? "x64" : "x86")
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_MAC
#define OS_MACOS1
#elif TARGET_OS_IPHONE
#define OS_IOS1
#endif
#define OS_ARCH "ARM/x64"
#else
#define OS_UNKNOWN1
#endif
// === Utility Macros ===
#ifdef _MSC_VER
#define NOINLINE __declspec(noinline)
#define FORCEINLINE __forceinline
#else
#define NOINLINE __attribute__((noinline))
#define FORCEINLINE __attribute__((always_inline)) inline
#endif
#endif // PLATFORM_DETECT_H
Usage Example:
#include "platform_detect.h"
#include <iostream>
int main() {
std::cout << "Compiler: " << COMPILER_NAME
<< " (ver=" << COMPILER_VERSION << ")\n";
#ifdef OS_WINDOWS
std::cout << "Running on Windows (" << OS_ARCH << ")\n";
#endif
#ifdef COMPILER_MSVC
#if _MSC_VER >= 1900
auto lambda = []() { return 42; }; // C++11 OK
#endif
#endif
return 0;
}
6. Common Pitfalls and Best Practices
6.1 Pitfall 1: Confusing Visual Studio Version with MSVC Version
- Incorrect: Assuming that the
<span>_MSC_VER</span>for VS2019 is 2019. - Correct: VS2019 corresponds to
<span>_MSC_VER = 1920~1929</span>.
6.2 Pitfall 2: Using Numerical Comparison of Strings in .pro
- Incorrect:
<span>MSC_VER > 1900</span>(in qmake, this is string comparison) - Correct:
<span>greaterThan(MSC_VER, 1900)</span>
6.3 Best Practices
- Prefer using standard feature detection (such as
<span>__has_include</span>,<span>__cplusplus</span>). - Avoid over-reliance on version numbers, and instead use feature detection (e.g.,
<span>#ifdef _WIN32</span>instead of<span>#if _MSC_VER >= xxx</span>). - Test multiple compiler combinations in CI/CD (GCC 7/11, Clang 10/15, MSVC 1910/1930).
7. Conclusion
Mastering precise detection techniques for compilers and operating systems is fundamental to building robust, portable C++ applications. Whether using preprocessor macros for source-level conditional compilation or configuring build logic in qmake/CMake, understanding these underlying details can significantly enhance development efficiency and software quality.
Other
- Domestic Open Source:https://gitee.com/feiyangqingyun
- International Open Source:https://github.com/feiyangqingyun
- Project Collection:https://qtchina.blog.csdn.net/article/details/97565652
- Contact: WeChat feiyangqingyun
- Official Store:https://shop114595942.taobao.com/