检查构造的常量是否为#define [英] Check if a constructed constant is #define'd
问题描述
我正在尝试建立一个测试,以检查某个文件是否定义了具有特定名称空间的标头保护.由于测试是通用的,因此仅在编译时才知道此名称空间,并以 -DTHENAMESPACE = BLA
的形式传入.然后,我们使用 https://stackoverflow.com/a/1489985/1711232 中的一些魔术贴将它们粘贴在一起.>
这意味着我想做类似的事情:
<代码> #define PASTER(x,y)x ## _ ## y#定义评估者(x,y)粘贴者(x,y)#定义NAMESPACE(有趣)的评估者(THENAMESPACE,有趣)#ifndef NAMESPACE(API_H)//评估为BLA_API_H#错误命名空间定义不正确"#万一
但是这不能正常工作,因为cpp抱怨 ifndef
不需要括号.
如果有可能的话,我该如何正确地做到这一点?
我也尝试添加更多的间接层,但是没有取得很大的成功.
因此,直接执行至少正确地执行 #ifdef
似乎是不可能的:
考虑 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
),这也将使我的测试管道能够解决此问题.
如果我们假设包含后卫总是像这样
#define NAME/*此处不再有令牌*/
和如果如您所说的那样,任何编译时错误(而不是排他地是 #error
)都是可以接受的,那么您可以执行以下操作:
#define THENAMESPACE BLA#define BLA_API_H//注释掉以获得错误.#定义CAT(x,y)CAT_(x,y)#定义CAT_(x,y)x ## y#定义NAMESPACE(x)静态int CAT(UNUSED _,__ LINE__)= CAT(CAT(THENAMESPACE,CAT(_,x)),+ 1);NAMESPACE(API_H)
在这里, NAMESPACE(API_H)
尝试使用 ##
连接 BLA_API_H
和 +
.
这会导致错误:如果
BLA_API_H
为 除了> #define
设置为无令牌".
在存在 #define BLA_API_H
的情况下, NAMESPACE(API_H)
变成
static int UNUSED _/*此处的行号*/= +1;
如果您选择了一个不太可靠的解决方案,甚至可以得到不错的错误消息:
#define THENAMESPACE BLA#define BLA_API_H//注释掉以获得错误.#定义TRUTHY_VALUE_X 1#定义CAT(x,y)CAT_(x,y)#定义CAT_(x,y)x ## y#定义NAMESPACE(x)CAT(CAT(TRUTHY_VALUE_,CAT(THENAMESPACE,CAT(_,x))),X)#if!NAMESPACE(API_H)#error命名空间定义不正确"#万一
在这里,如果定义了 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
定义为真实内容,则会得到错误的内容.
I am trying to build a test that checks if a certain file defines a header guard with a certain namespace. Because the test is generic, this namespace is only known at compile-time and passed in as -DTHENAMESPACE=BLA
. We then use some magic from https://stackoverflow.com/a/1489985/1711232 to paste that together.
This means I want to do something like:
#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
But this does not work properly, with cpp complaining about the ifndef
not expecting the parentheses.
How can I do this properly, if it is possible at all?
I have also tried adding more layers of indirection, but not with a lot of success.
So directly, properly executing the #ifdef
this at least appears to not be possible:
Considering the defined
operator:
If the defined operator appears as a result of a macro expansion, the C standard says the behavior is undefined. GNU cpp treats it as a genuine defined operator and evaluates it normally. It will warn wherever your code uses this feature if you use the command-line option -Wpedantic, since other compilers may handle it differently. The warning is also enabled by -Wextra, and can also be enabled individually with -Wexpansion-to-defined.
https://gcc.gnu.org/onlinedocs/cpp/Defined.html#Defined
and ifdef
expects a MACRO, and does not do further expansion.
https://gcc.gnu.org/onlinedocs/cpp/Ifdef.html#Ifdef
But maybe it is possible to trigger an 'undefined constant' warning (-Wundef
), which would also allow my test pipeline to catch this problem.
If we assume that include guards always looks like
#define NAME /* no more tokens here */
and if, as you said, any compile time error (rather than #error
exclusively) is acceptable, then you can do following:
#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)
Here, NAMESPACE(API_H)
tries to concatenate BLA_API_H
and +
using ##
.
This results in error: pasting "BLA_API_H" and "+" does not give a valid preprocessing token
except if BLA_API_H
is #define
d to 'no tokens'.
In presence of #define BLA_API_H
, NAMESPACE(API_H)
simply becomes
static int UNUSED_/*line number here*/ = +1;
If you settle for a less robust solution, you can even get nice error messages:
#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
Here, if BLA_API_H
is defined, then #if !NAMESPACE(API_H)
expands to #if 1
.
If BLA_API_H
is not defined, then it expands to #if TRUTHY_VALUE_BLA_API_HX
, and TRUTHY_VALUE_BLA_API_HX
evaluates to false
due to being undefined.
The problem here is that if TRUTHY_VALUE_BLA_API_HX
accidentally turns out to be defined to something truthy, you'll get a false negatie.
这篇关于检查构造的常量是否为#define的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!