如何测试预处理器符号是否#define'd但没有值?

gal*_*ets 35 c++ visual-c++ c-preprocessor

使用C++预处理程序指令,是否可以测试是否已定义预处理程序符号但没有值?像这样的东西:

#define MYVARIABLE
#if !defined(MYVARIABLE) || #MYVARIABLE == ""
... blablabla ...
#endif
Run Code Online (Sandbox Code Playgroud)

编辑:我这样做的原因是因为我正在处理的项目应该从环境中获取一个字符串/DMYSTR=$(MYENVSTR),这个字符串可能是空的.如果用户忘记定义此字符串,我想确保项目无法编译.

Mar*_*ork 40

Soma宏魔法:

#define DO_EXPAND(VAL)  VAL ## 1
#define EXPAND(VAL)     DO_EXPAND(VAL)

#if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1)

Only here if MYVARIABLE is not defined
OR MYVARIABLE is the empty string

#endif
Run Code Online (Sandbox Code Playgroud)

请注意,如果在命令行上定义MYVARIABLE,则默认值为1

g++ -DMYVARIABLE <file>
Run Code Online (Sandbox Code Playgroud)

这里MYVARIABLE的值是1

g++ -DMYVARIABLE= <file>
Run Code Online (Sandbox Code Playgroud)

这里MYVARIABLE的值是空字符串

报价问题解决了:

#define DO_QUOTE(X)        #X
#define QUOTE(X)           DO_QUOTE(X)

#define MY_QUOTED_VAR      QUOTE(MYVARIABLE)

std::string x = MY_QUOTED_VAR;
std::string p = QUOTE(MYVARIABLE);
Run Code Online (Sandbox Code Playgroud)

  • 但是你将如何处理 `-DX="#"` 或 `-DY="a b"`? (4认同)
  • 或者更确切地说,如果它包含除"[A-Za-z _] [A-Za-z0-9 _]*以外的任何内容"以上将失败 (3认同)
  • 这很聪明,但是当实际定义“ MYVARIABLE”时,它将破坏构建,因为“ EXPAND(MYVARIABLE)”将返回非整数值,并且编译器将在“ #if!defined(MYVARIABLE)||处停止”。(EXPAND(MYVARIABLE)== 1)` (2认同)
  • **危险**:如果你的`#if`上面有一个`#define abcd 0`(因为那时`EXPAND(MYVARIABLE)`变成'01`,它不适用于`-DMYVARIABLE = abcd`)是**等于'1`).如果`MYVARIABLE`要保存用户定义的文件名,你永远不知道文件名是否实际上是定义为'0`(或'0x`,或'00`,...)的宏的名称,并且构建将失败. (2认同)
  • 对 Loki 的解决方案略有改进的是`#define NO_OTHER_MACRO_STARTS_WITH_THIS_NAME_`、`#define IS_EMPTY(name) defined(NO_OTHER_MACRO_STARTS_WITH_THIS_NAME_ ## name)`、`#define EMPTY(name) IS_EMPTY(name)`、`#if ! || 空(我的变量)`。如果`MYVARIABLE` 是`0`、`0x` 或`00` 等(或定义为其中之一的宏的名称),这也有效。 (2认同)

Geo*_*che 10

如果用户忘记定义此字符串,我想确保项目无法编译.

虽然我在之前的构建步骤中检查了这一点,但您可以在编译时执行此操作.使用Boost简洁:

#define A "a"
#define B
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(A)) > 1); // succeeds
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(B)) > 1); // fails
Run Code Online (Sandbox Code Playgroud)


小智 10

我没有看到这个问题的解决方案,但我很惊讶它不常用.它似乎适用于Xcode Objc.区分"无值定义"和"定义集0"

#define TRACE
#if defined(TRACE) && (7-TRACE-7 == 14)
#error TRACE is defined with no value
#endif
Run Code Online (Sandbox Code Playgroud)

  • 如果`#define TRACE -14`将无法工作:( (8认同)
  • 可以修复`#define TRACE -14`的情况`#if defined(TRACE) &amp;&amp; (TRACE+0 != -14) &amp;&amp; (7-TRACE-7 == 14)` (3认同)

db-*_*inf 5

Mehrad的答案必须扩展才能使其起作用。还有他的评论

/ * MYVARI(A)BLE在这里未定义* /

是不正确的; 要测试未定义的变量,可以使用简单的test #ifndef MYVARIABLE

然而,经过这样的测试,他的表情导致了对原始问题的正确解决。我测试了此代码对于宏MYVARIABLE的未定义,已定义但为空和非空的值有效:

#ifndef MYVARIABLE
    /* MYVARIABLE is undefined here */
#elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
    /* MYVARIABLE is defined with no value here */
#else
    /* MYVARIABLE is defined here */
#endif
Run Code Online (Sandbox Code Playgroud)

#elif语句的~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1 工作方式如下:

  • MYVARIABLE定义但为空时,它将扩展为~(~+0) == 0 && ~(~+1) == 1,从而得出结果0==0 && 1==1(双重否定是身份运算符)。
  • MYVARIABLE定义为数值(例如n)时,它将扩展为~(~n+0)==0 && ~(~n+1)==1。在的左侧&&,表达式的~(~n+0)==0计算结果为n==0。但是,用n==0,右手边的计算结果为~(~0+1)==1,〜0为-1对~(-1+1)==1,然后~0==1最后-1==1,这显然是错误的。
  • MYVARIABLE定义为非数字值时,预编译器将所有未知符号减少为0,并且我们再次得到n == 0的前一种情况。

我完整的测试代码(另存为文件test.c):

#include <stdio.h>

int main() {
    printf("MYVARIABLE is "
#ifndef MYVARIABLE
     "undefined"
#elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
     "defined without a value"
#else 
     "defined with this value : %i", MYVARIABLE
#endif
    );
    printf("\n");
}
Run Code Online (Sandbox Code Playgroud)

使用GNU预处理程序cpp,您可以尝试查看生成的代码:

# undefined
cpp test.c
#defined without a value
cpp -DMYVARIABLE= test.c
#defined wit an implicit value 1
cpp -DMYVARIABLE test.c
#defined wit an explicit value 1
cpp -DMYVARIABLE=1 test.c
#defined wit an explicit value a
cpp -DMYVARIABLE=a test.c
Run Code Online (Sandbox Code Playgroud)

或编译和执行的输出(在某些Linux下)

$ gcc -o test test.c ; ./test
MYVARIABLE is undefined
$ gcc -DMYVARIABLE= -o test test.c ; ./test
MYVARIABLE is defined without a value
$ gcc -DMYVARIABLE -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=1 -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=a -o test test.c ; ./test
test.c: In function ‘main’:
<command-line>:0:12: error: ‘a’ undeclared (first use in this function)
...
Run Code Online (Sandbox Code Playgroud)

在最后一次运行中,MYVARIABLE被定义为“ a”,该错误不是宏定义中的错误;宏正确地导致了最后一种情况,“用此值定义...”。但是此值是“ a”,而在代码中未定义“ a”,因此编译器或过程必须发出信号。

这样,最后一种情况就是一个很好的例子,说明了原始问题的意图为何非常危险:用户可以通过宏在要编译的代码中引入任何程序行序列。检查是否未引入此类代码,需要对宏进行有效值的更多检查。可能需要完整的脚本,而不是将此任务留给预处理。在这种情况下,在预处理中检查它又有什么用?