C++条件编译用法详解(附带实例)
条件编译是一种简单的机制,它使 C++ 开发人员能够维护单个代码库,但只考虑编译代码的某些部分,以生成不同的可执行文件(通常是为了在不同的平台或硬件上运行,或依赖于不同的库或版本)。
常见的例子包括使用或忽略基于编译器、平台(x86、x64、ARM等)、配置(调试或发布)或任何用户自定义的特定条件的代码。
条件编译是一种广泛使用的技术。本文,我们将看几个例子并解释它们是如何工作的。
条件编译的一般形式如下:
1) 在源代码中使用 #define 指令:
2) 使用特定于每个编译器的编译器命令行选项。使用最广泛的编译器的选项示例如下:
以下是条件编译的典型示例:
1) 头文件保护可以避免重复定义:
2) 针对跨平台应用程序的特定编译器代码,下面是一个将带有编译器名称的消息打印到控制台的示例:
3) 针对多个架构的目标特定代码,例如针对多个编译器和架构有条件地编译代码:
4) 特定于配置的代码,例如用于有条件地编译调试和发布版本的代码:
当使用预处理指令 #if、#ifndef、#ifdef、#elif、#else 和 #endif 时,编译器将至多选择一个分支,其主体将包含在编译单元中。这些指令的主体可以是任何文本,包括其他预处理指令。适用规则如下:
由于头文件通常被包含在多个源文件中,如果编译包含头文件的每个编译单元,则会产生重复定义,这是错误的。因此,按照前面给出的示例所示的方式,对头文件中的代码进行多次编译保护。
分析前面的示例,它的工作方式是,如果宏 UNIQUE_NAME(这是通用名称)没有定义,那么 #if 指令之后的代码(直到 #endif)将被包含在编译单元中并被编译。当发生这种情况时,宏 UNIQUE_NAME 用 #define 指令定义,下次在编译单元中包含头文件时,宏 UNIQUE_NAM E已定义,#if 指令体中的代码不应包含在编译单元中,因此不会再次编译。
注意,宏的名称在整个应用程序中必须是唯一的,否则,将只编译使用宏的第一个头文件中的代码。其他使用相同名称的头文件中的代码将被忽略。通常,宏的名称基于定义它的头文件的名称进行定义。
注意,在前面的示例中,我们只考虑了几种架构。在实践中,可以使用多个宏来标识相同的架构。在代码中使用这些类型的宏之前,请确保阅读每个编译器的文档。
常见的例子包括使用或忽略基于编译器、平台(x86、x64、ARM等)、配置(调试或发布)或任何用户自定义的特定条件的代码。
条件编译是一种广泛使用的技术。本文,我们将看几个例子并解释它们是如何工作的。
C++条件编译的具体实现
在 C++ 若要条件编译部分代码,可以使用 #if、#ifdef 和 #ifndef 指令(与 #elif、#else 和 #endif 指令一起)。条件编译的一般形式如下:
#if condition1 text1 #elif condition2 text2 #elif condition3 text3 #else text4 #endif要定义用于条件编译的 condition 宏,可以使用以下方法之一:
1) 在源代码中使用 #define 指令:
#define VERBOSE_PRINTS #define VERBOSITY_LEVEL 5
2) 使用特定于每个编译器的编译器命令行选项。使用最广泛的编译器的选项示例如下:
- 对于 VC++,使用 /Dname 或 /Dname=value(其中 /Dname 等价于 /Dname=1),例如,cl /DVERBOSITY_LEVEL=5。
- 对于 GCC 和 Clang,使用 -D name 或 -D name=value(其中 -D name 等价于 -D name=1),例如,gcc -D VERBOSITY_LEVEL=5。
以下是条件编译的典型示例:
1) 头文件保护可以避免重复定义:
#ifndef Defined(UNIQUE_NAME) #define UNIQUE_NAME class widget { }; #endif
2) 针对跨平台应用程序的特定编译器代码,下面是一个将带有编译器名称的消息打印到控制台的示例:
void show_compiler() { #if defined _MSC_VER std::cout << "Visual C++\n"; #elif defined __clang__ std::cout << "Clang\n"; #elif defined __GNUG__ std::cout << "GCC\n"; #else std::cout << "Unknown compiler\n"; #endif }
3) 针对多个架构的目标特定代码,例如针对多个编译器和架构有条件地编译代码:
void show_architecture() { #if defined _MSC_VER #if defined _M_X64 std::cout << "AMD64\n"; #elif defined _M_IX86 std::cout << "INTEL x86\n"; #elif defined _M_ARM std::cout << "ARM\n"; #else std::cout << "unknown\n"; #endif #elif defined __clang__ || __GNUG__ #if defined __amd64__ std::cout << "AMD64\n"; #elif defined __i386__ std::cout << "INTEL x86\n"; #elif defined __arm__ std::cout << "ARM\n"; #else std::cout << "unknown\n"; #endif #else #error Unknown compiler #endif }
4) 特定于配置的代码,例如用于有条件地编译调试和发布版本的代码:
void show_configuration() { #ifdef _DEBUG std::cout << "debug\n"; #else std::cout << "release\n"; #endif }
当使用预处理指令 #if、#ifndef、#ifdef、#elif、#else 和 #endif 时,编译器将至多选择一个分支,其主体将包含在编译单元中。这些指令的主体可以是任何文本,包括其他预处理指令。适用规则如下:
- #if、#ifdef 和 #ifndef 必须用 #endif 匹配。
- #if 指令可以有多条 #elif 指令,但只有一条 #else 指令,且 #else 必须是 #endif 之前的最后一条。
- #if、#ifndef、#elif、#else 和 #endif 可以嵌套。
- #if 指令需要一个常量表达式,而 #ifdef 和 #ifndef 则需要一个标识符。
- defined 操作符可以用于预处理器常量表达式,但只能在 #if 和 #elif 指令中使用。
- defined(identifier) 在定义 identifier 时为 true,否则,它被认为是 false。
- 定义为空文本的标识符被认为是有定义的。
- #ifdef identifier 等价于 #if defined(identifier)。
- #ifndef identifier 等价于 #if !defined(identifier)。
- defined(identifier) 和 defined identifier 是等价的。
C++条件编译的应用场景
1) 防止头文件重复编译
头文件保护是条件编译最常见的形式之一,这种技术主要用于防止头文件的内容被多次编译(为了检测应包含什么内容,头文件仍然每次都会被扫描)。由于头文件通常被包含在多个源文件中,如果编译包含头文件的每个编译单元,则会产生重复定义,这是错误的。因此,按照前面给出的示例所示的方式,对头文件中的代码进行多次编译保护。
分析前面的示例,它的工作方式是,如果宏 UNIQUE_NAME(这是通用名称)没有定义,那么 #if 指令之后的代码(直到 #endif)将被包含在编译单元中并被编译。当发生这种情况时,宏 UNIQUE_NAME 用 #define 指令定义,下次在编译单元中包含头文件时,宏 UNIQUE_NAM E已定义,#if 指令体中的代码不应包含在编译单元中,因此不会再次编译。
注意,宏的名称在整个应用程序中必须是唯一的,否则,将只编译使用宏的第一个头文件中的代码。其他使用相同名称的头文件中的代码将被忽略。通常,宏的名称基于定义它的头文件的名称进行定义。
2) 实现跨平台
条件编译的另一个重要示例是跨平台代码,它需要考虑到不同的编译器和架构(通常是 Intel x86、AMD64 或 ARM之一)。然而,编译器针对可能的平台定义了自己的宏,前面的示例展示了如何有条件地针对多个编译器和架构编译代码。注意,在前面的示例中,我们只考虑了几种架构。在实践中,可以使用多个宏来标识相同的架构。在代码中使用这些类型的宏之前,请确保阅读每个编译器的文档。
3) 配置程序
和配置相关的代码也应该用宏和条件编译处理。像 GCC 和 Clang 这样的编译器没有针对调试配置定义任何特殊的宏(当使用 -g 标志时)。VC++ 确实针对调试配置定义了 _DEBUG,这在前面的示例中已经展示过。对于其他编译器,必须显式地定义一个宏来标识这样的调试配置。相关文章
- C语言#if、#ifdef、#ifndef用法详解,C语言条件编译详解
- C语言#if、#elif、#else和#endif条件编译的用法(非常详细)
- C语言条件编译的概念和用法(非常详细)
- C语言#ifdef和#ifndef条件编译的用法(附带实例)
- C语言条件编译#if、#ifdef和#ifndef的用法(附带实例)
- C语言#if、#elif、#else和#endif条件编译的用法(附带实例)
- C++条件编译详解
- C语言条件编译(#if,#ifdef,#ifndef,#endif,#else,#elif)
- C语言条件编译(#if、#else、#elif、#ifdef和#ifndef)详解
- 条件编译,C语言条件编译详解