system_judge - 系统判断宏¶
system_judge.h 定义了平台和架构检测的底层宏,是整个项目跨平台支持的基础。这个文件的设计原则很简单——只负责检测,不做任何决策,把判断结果以宏的形式暴露给上层使用。
操作系统检测¶
我们支持三个主流桌面操作系统,检测基于编译器预定义宏:
// Windows 平台检测(包括 32 位和 64 位)
#if defined(_WIN32) || defined(_WIN64)
# define CFDESKTOP_OS_WINDOWS
#endif
// Linux 平台检测
#if defined(__linux__)
# define CFDESKTOP_OS_LINUX
#endif
Windows 的检测用了两个宏是因为 _WIN32 在 64 位 Windows 上也会被定义,而 _WIN64 只在 64 位上定义。用 || 连接可以覆盖所有情况。Linux 的 __linux__ 则是所有类 Linux 系统通用的宏,如果你需要区分发行版,得在运行时检查 /etc/os-release。
架构检测¶
CPU 架构检测主要服务于性能优化和 SIMD 指令使用:
// x86-64 (AMD64) 检测
#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
# define CFDESKTOP_ARCH_X86_64
#endif
// ARM64 检测
#if defined(__aarch64__) || defined(_M_ARM64)
# define CFDESKTOP_ARCH_ARM64
#endif
// ARM32 检测
#if defined(__arm__) || defined(_M_ARM)
# define CFDESKTOP_ARCH_ARM32
#endif
这里的 __x86_64__ 和 __aarch64__ 是 GCC/Clang 的约定,_M_X64、_M_ARM64、_M_ARM 是 MSVC 的约定。用多个宏检查是因为不同编译器的命名习惯不一样。
宏定义规则¶
每个宏定义都遵循相同的模式——检测到条件后定义一个不带值的标准宏:
不带值的设计是有意为之的。我们只需要知道"是不是这个平台",不需要传递额外信息。如果需要细分版本(比如 Windows 10 vs Windows 11),应该在运行时检测,而不是用编译时宏。
使用示例¶
#include "base/macro/system_judge.h"
// 根据平台选择不同的实现
#if defined(CFDESKTOP_OS_WINDOWS)
// Windows 实现
#elif defined(CFDESKTOP_OS_LINUX)
// Linux 实现
#else
#error "Unsupported platform"
#endif
// 根据架构选择优化路径
#if defined(CFDESKTOP_ARCH_X86_64)
// 使用 AVX2 指令集
#elif defined(CFDESKTOP_ARCH_ARM64)
// 使用 NEON 指令集
#endif
⚠️ 记得在 #else 或 #elif 分支加上 #error,避免在不支持的平台上静默编译通过,结果运行时出错。
编译器兼容性¶
这些宏在主流编译器上都能工作:
| 编译器 | 平台宏支持 | 架构宏支持 |
|---|---|---|
| MSVC | 完全支持 | 完全支持 |
| GCC | 完全支持 | 完全支持 |
| Clang | 完全支持 | 完全支持 |
| MinGW | 完全支持 | 完全支持 |
如果你用了其他编译器(比如 Intel ICC),可能需要额外添加检测逻辑。
扩展新平台¶
添加新平台支持时,需要在两个地方修改代码:
// 1. 在 system_judge.h 添加检测
#if defined(__NEW_OS__)
# define CFDESKTOP_OS_NEW_OS
#endif
// 2. 在对应实现文件添加平台特定代码
#if defined(CFDESKTOP_OS_NEW_OS)
// 新平台实现
#endif
记得同步更新文档和测试,确保 CI 环境能在新平台上正常运行。
常见问题¶
为什么不用 C++20 的 #ifdef?因为我们需要支持 C++17,而且 std::is_constant_evaluated() 只能判断是否在常量求值上下文,不能区分平台。
为什么不用 CMake 的 CMAKE_SYSTEM_NAME?因为那是构建系统的信息,不能直接在代码里用。我们用宏是为了在预处理阶段就知道平台,实现条件编译。