首页 > 解决方案 > 检查构造的常量是否为#define'd

问题描述

我正在尝试构建一个测试来检查某个文件是否定义了具有某个命名空间的标头保护。因为测试是通用的,所以这个命名空间只在编译时是已知的,并以-DTHENAMESPACE=BLA. 然后我们使用https://stackoverflow.com/a/1489985/1711232中的一些魔法将它们粘贴在一起。

这意味着我想做类似的事情:

#define PASTER(x, y) x##_##y
#define EVALUATOR(x, y) PASTER(x, y)
#define NAMESPACE(fun) EVALUATOR(THENAMESPACE, fun)
#ifndef NAMESPACE(API_H)  // evaluates to BLA_API_H
#  error "namespace not properly defined"
#endif

但这不能正常工作,cpp 抱怨ifndef不期望括号。

如果可能的话,我该如何正确地做到这一点?

我也尝试过添加更多的间接层,但并没有取得很大的成功。


如此直接地,正确执行#ifdefthis 至少似乎是不可能的:

考虑defined运营商:

如果定义的运算符作为宏扩展的结果出现,则 C 标准说行为是未定义的。GNU cpp 将其视为真正定义的运算符并正常评估它。如果您使用命令行选项 -Wpedantic,它会在您的代码使用此功能的任何地方发出警告,因为其他编译器可能会以不同的方式处理它。该警告也可以通过 -Wextra 启用,也可以通过 -Wexpansion-to-defined 单独启用。

https://gcc.gnu.org/onlinedocs/cpp/Defined.html#Defined

ifdef期望一个 MACRO,并且不做进一步的扩展。

https://gcc.gnu.org/onlinedocs/cpp/Ifdef.html#Ifdef

但也许有可能触发“未定义的常量”警告 ( -Wundef),这也将允许我的测试管道捕捉到这个问题。

标签: cc-preprocessor

解决方案


如果我们假设包含警卫总是看起来像

#define NAME /* no more tokens here */

如果,如您所说,任何编译时错误(而不是#error排他性的)都是可以接受的,那么您可以执行以下操作:

#define THENAMESPACE BLA
#define BLA_API_H // Comment out to get a error.

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

#define NAMESPACE(x) static int CAT(UNUSED_,__LINE__) = CAT(CAT(THENAMESPACE,CAT(_,x)),+1);

NAMESPACE(API_H)

在这里,NAMESPACE(API_H)尝试连接BLA_API_H+使用##.

这导致error: pasting "BLA_API_H" and "+" does not give a valid preprocessing token except if BLA_API_His #defined 为“无标记”。

在存在的情况下#define BLA_API_HNAMESPACE(API_H)简单地变成

static int UNUSED_/*line number here*/ = +1;

如果你满足于一个不太健壮的解决方案,你甚至可以得到很好的错误消息:

#define THENAMESPACE BLA
#define BLA_API_H // Comment out to get a error.

#define TRUTHY_VALUE_X 1
#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

#define NAMESPACE(x) CAT(CAT(TRUTHY_VALUE_,CAT(THENAMESPACE,CAT(_,x))),X)

#if !NAMESPACE(API_H)
#error "namespace not properly defined"
#endif

在这里,如果BLA_API_H已定义,则#if !NAMESPACE(API_H)扩展为#if 1

如果BLA_API_H未定义,则扩展为#if TRUTHY_VALUE_BLA_API_HX,并且由于未定义而TRUTHY_VALUE_BLA_API_HX计算为。false

这里的问题是,如果TRUTHY_VALUE_BLA_API_HX意外地被定义为真实的东西,你会得到一个错误的否定。


推荐阅读