检查构造的常量是否为#define [英] Check if a constructed constant is #define'd

查看:52
本文介绍了检查构造的常量是否为#define的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试建立一个测试,以检查某个文件是否定义了具有特定名称空间的标头保护.由于测试是通用的,因此仅在编译时才知道此名称空间,并以 -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 #defined 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆