为什么人们使用#ifdef进行function标记测试?
人们建议#ifdef
进行大范围的条件编译 。 搜索#ifdef
证实它的使用是普遍的。
然而#ifdef NAME
(或等效的#if defined(NAME)
和相关的#ifndef NAME
(和#if !defined(NAME)
)有一个严重的缺陷:
header.h
#ifndef IS_SPECIAL #error You're not special enough #endif
source.cpp
#include "header.h"
gcc -DIS_SPECIAL source.cpp
显然,将会通过
source1.cpp
#define IS_SPECIAL 1 #include "header.h"
但是,也一样
source0.cpp
#define IS_SPECIAL 0 #include "header.h"
这是完全错误的事情。 一些C ++编译器,传递一个以C模式处理的文件(由于扩展或命令行选项)有效地执行#define __cplusplus 0
。 我看到事情破裂了
#ifdef __cplusplus extern "C" { #endif /* ... */ #ifdef __cplusplus } #endif
在C模式下处理,其中extern "C"
是无效语法,因为__cplusplus
实际上自动定义为0
。
另一方面,这适用于所有编译器:
#if __cplusplus extern "C" { #endif /* ... */ #if __cplusplus } #endif
为什么人们在这种情况下仍然使用#ifdef
? 他们是否完全没有意识到#if
在未定义的名称上运行得非常好? 或者#if
vs #ifdef
是否存在条件编译的实际缺点?
显然, #ifdef
确实有有效用途,例如为可配置参数提供默认值:
#ifndef MAX_FILES #define MAX_FILES 64 #endif
我只讨论旗帜测试的情况。
为什么人们在这种情况下仍然使用#ifdef?
个人意见:从命令行控制起来稍微容易一些。 我更喜欢-DOPTION
over -DOPTION=1
。
此外,名称的存在显然是二进制的。 我不必处理{0,非零,未定义}。
他们是否完全没有意识到#if在未定义的名称上运行得非常好?
我不知道。 这有什么语义? 假定未定义的名称是0吗? 我是否想要向那些几乎不了解预处理器的人解释一下?
或者#if vs #ifdef是否存在条件编译的实际缺点?
对我来说,名称存在的#ifdef/#ifndef
的二元性质是一个明确的好处。 另外,我对这两种构造的主要用法是包含警戒。 使用#ifndef
这种模式最干净。
我不能说为什么一般人更喜欢#ifdef
不是#if
,但我至少可以说我为什么这样做。 基于刚才的内省(自从你问过 – 我以前从未明确考虑过它),有两个原因:
1)我更喜欢我的宏(我试图谨慎使用)尽可能地使用最直接的语义,并相应地尽可能地“免费”。 我假设宏,如果它们有任何类型,都是“类型自由函数”(注意:这里我更喜欢模板,但有时候一切……)或基本上只是布尔标志。 因此,即使为宏指定值1
,也会为我拉伸它。 (例如,如果你有#define _cplusplus 2
,它应该是什么意思?它应该以不同于1
?)
2)最后一点关于它们是“标志”的事实是我主要将这些用于我在命令行(或在IDE中)指定的条件编译标志 。 实际上,在我的软件团队中,当我们编写C ++时,基本上“禁止”将宏用于其他任何事情。 即使在条件编译的情况下,如果我们可以通过其他方式解决问题(例如通过模块化),我们也会尝试避免它们。
这两个原因都与同样的基本假设有关,即尽可能避免使用宏(在C ++中),因此不需要类型的复杂性或不透明的语义。 如果你没有做出这个假设(并且在用C语言编程时它不太常见,我知道),那么这会改变一些事情,我想你对#if
的观点可能会有更多的影响。