【发布时间】:2015-01-23 08:58:38
【问题描述】:
背景
我有一个C项目,它有一个配置头文件,例如:
// config.h
#define FEATURE_1_AVAILABLE 1
#define FEATURE_2_AVAILABLE 0
#define FEATURE_3_AVAILABLE 1
模块使用这些标志在构建中包含/排除功能:
#include "config.h"
void foo(void) {
...
#if FEATURE_1_AVAILABLE
useFeature1();
#endif
...
config.h 针对不同的构建有不同的版本。
问题
#include "config.h" 被意外删除,因此即使在配置中启用该功能也未激活。
不幸的是,#if FEATURE_1_AVAILABLE 在未定义时会默默地评估为 0。所以如果没有定义,我需要一个错误。
选项 1
我可以在每个条件下添加额外的“如果定义”测试。
#ifndef FEATURE_1_AVAILABLE
#error No feature 1
#elif FEATURE_1_AVAILABLE
useFeature1();
#endif
缺点是一个特性标志可能会在模块中许多不同的地方进行测试,并且添加多个预处理器行不会使代码更漂亮。
选项 2
在模块开头添加单项测试:
#ifndef FEATURE_1_AVAILABLE
#error No feature 1
#endif
但这可能会因为与#include "config.h" 相同的原因而丢失/忘记。
选项 3
将标志定义为宏:
#define FEATURE_1_AVAILABLE() 1
并用作:
#if FEATURE_1_AVAILABLE()
不幸的是,如果您忘记了括号,这也会静默失败:
#if FEATURE_1_AVAILABLE
选项 X
还有更好的选择吗?如果符号未定义或写入不正确,则会在编译时给出错误消息。并且希望不会使代码看起来难以维护,预处理器混乱。
其他事情
- 无法进行运行时检查。禁用的功能不得在二进制文件中。
- 特征必须在文件中定义。将它们定义为全局编译器选项还有其他问题。
- 这适用于嵌入式系统。其中一些功能需要硬件才能工作,并且测试非常耗时。所以最好在编译时出错。
【问题讨论】:
-
找到负责删除包含的人,然后将那个人放在头上。如果您确实需要在所有源文件中包含该特定头文件,那么所有开发人员都需要知道它。此外,自动化单元测试应该会自动检测到此类问题(您确实进行过自动化单元测试吗?),并且在程序到达客户之前就已修复。
-
@JoachimPileborg 但我不想打自己 :) 它被意外删除了。关于单元测试:正如我所写,在没有硬件的情况下测试其中一些是不可能的。因此,在编译时检测到此类错误时,修复成本会更低。
-
@JimBalter 快速查看可能是骗人的。如果是,我会进一步查看并关闭它。
标签: c c-preprocessor preprocessor-directive