Gcc 在C if语句中使用已定义的(宏)

Gcc 在C if语句中使用已定义的(宏),gcc,c-preprocessor,Gcc,C Preprocessor,我想用C编写类似这样的代码: if(defined(MACRO)) ... else ... 如果(已定义(宏)) ... 其他的 ... 但是我在C中找不到任何方法来实现这一点,因为定义的(宏)预处理器操作符只在#if内工作。有办法做到这一点吗 我真正喜欢做的是写: ASSERT(UART, var >= 0); 断言(UART,var>=0); 在哪里 #定义断言(名称、测试)\ 做{\ if(定义(名称)和(测试))\ printf(“断言失败”)\ }而(0) 因此,我可以在定义

我想用C编写类似这样的代码:

if(defined(MACRO)) ... else ... 如果(已定义(宏)) ... 其他的 ... 但是我在C中找不到任何方法来实现这一点,因为定义的(宏)预处理器操作符只在#if内工作。有办法做到这一点吗

我真正喜欢做的是写:

ASSERT(UART, var >= 0); 断言(UART,var>=0); 在哪里

#定义断言(名称、测试)\ 做{\ if(定义(名称)和(测试))\ printf(“断言失败”)\ }而(0) 因此,我可以在定义宏时启用断言检查,如果未定义宏,则不应检查断言。如果您尝试这样做,您会得到:

implicit declaration of function `defined' 函数“defined”的隐式声明
这是可以理解的,因为GCC编译器找不到
defined()
预处理器操作符。

为什么不根据宏的不同定义
ASSERT

#ifdef MACRO
#define ASSERT(NAME, TEST) \
    do { \
        printf("Assert failed"); \
    } while(0)
#else
#define ASSERT(NAME, TEST) {}
#endif
应该避免在C条件中使用固定的预处理器值——当然编译器应该优化死代码,但是当您可以从本质上删除实际的C代码时,为什么还要依赖于它呢

编辑:

有一个涉及宏参数字符串化的相当丑陋的技巧,您可以使用:

#include <string.h>
#include <stdio.h>

#define X

#define ERROR_(NAME, TEXT) \
        if (strcmp("", #NAME) == 0) \
                printf("%s\n", TEXT)
#define ERROR(n, t) ERROR_(n, t)

int main() {
    ERROR(X, "Error: X");
    ERROR(Y, "Error: Y");

    return 0;
}
本质上,它使用这样一个事实,即当预处理器标记未定义为宏时,它会扩展到自身。另一方面,当定义它时,它将扩展为空字符串或其定义。除非有一个宏有自己的名字作为定义,否则这种破解应该是可行的

免责声明:使用本产品的风险自负

(…因为我肯定不会使用它!)

编辑2:

上述程序的
gcc-O0-S
汇编输出为:

        .file   "test.c"
        .section        .rodata
.LC0:
        .string "Error: X"
        .text
.globl main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movl    $.LC0, %edi
        call    puts
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.3"
        .section        .note.GNU-stack,"",@progbits
即使没有优化,GCC也将该程序简化为一个
put()
调用。此程序生成完全相同的程序集输出:

#include <stdio.h>

int main() {
    puts("Error: X");

    return 0;
}
#包括
int main(){
看跌期权(“错误:X”);
返回0;
}

因此,您可能不会有任何性能问题,这取决于您的编译器和任何优化…

好的,根据上一篇文章,我得到了这个想法,它似乎是可行的:

#define DEFINEDX(NAME) ((#NAME)[0] == 0) #define DEFINED(NAME) DEFINEDX(NAME) #定义定义的X(名称)(#名称)[0]==0) #定义已定义(名称)已定义X(名称) 这将检查名称是否已定义,因此它扩展为第一个字符为0的空字符串,或者它未定义,在这种情况下,它不是空字符串。这适用于GCC,因此可以编写

if( DEFINED(MACRO) ) ... 如果(已定义(宏)) ... 如果参数定义为1,则宏by将扩展为1。否则将扩展为0:

#define is_set(macro) is_set_(macro)
#define macrotest_1 ,
#define is_set_(value) is_set__(macrotest_##value)
#define is_set__(comma) is_set___(comma 1, 0)
#define is_set___(_, v, ...) v
您可以按如下方式使用它:

if (is_set(MACRO)) {
   /* Do something when MACRO is set */
}
说明: 这个技巧是基于变量函数,比如宏(
)和预处理器令牌连接(

  • is_set
    只是一个包装器,用于方便其参数的扩展
  • is\u set
    尝试将
    macrotest
    与其输入的评估值(
    comma
    )连接起来。 如果定义了它的输入,那么它就工作了;否则,
    is_集
    macrotest
    调用,其中
    is_集
    的原始参数(例如,
    is_集(foo)
    如果
    foo
    不是定义的宏,则导致
    macrotest_foo

  • is\u set\u\u
    中,其参数再次展开,但这仅在通过
    macrotest\u 1
    时才有效。 如果是,则调用
    is\u set(,1,0)
    ,因为
    逗号的计算结果为
    (请注意3个参数!)。 对于逗号的任何其他值(即,如果要测试的宏未定义或具有除
    1
    以外的任何其他(扩展)值,则无法扩展参数,因此调用
    is\u set\u(macrotest\u1,0)
    ,该参数只有2个参数

  • 最后,
    is\u set\u\u
    只需为其“输出”选择第二个参数,并删除所有其他参数。 由于
    is\u set\u
    的行为,如果要测试的宏已定义且为1,则会导致
    1
    ,否则会导致
    0


  • 不,这不好。我总是想定义ASSERT,但如果名称未定义,那么我不想做任何事情。此外,我不想用#ifdef@Miklos马罗蒂:所以名称的定义不是全局的,而断言是全局的,你想让断言只在名称被定义时工作吗?我想写这样的代码:ASSERT(UART,var>=0);断言(SPI,var==0);因此,如果我定义了UART,那么所有的UART断言都应该打开;如果我定义了SPI,那么所有的SPI断言都应该打开,我不想要预定义的固定数量的子系统,即ASSERT_UART,ASSERT_SPI不是一个好的解决方案。@Miklos Maroti:我想总是将这些宏定义为0或1不是你想要的是吗?是的,我不想定义任何一个条件,因此所有条件都被禁用。但请记住,这可能会重复使用C语句,并且只能在预编译器中进行部分扩展。据我所知,C编译器随后意识到,条件的结果是静态的,并消除了未使用的代码……很酷,但它只能工作对于空宏…也就是说,如果您定义my,那么DEFINED(M)将返回0(使用gcc 4.5.1进行测试),引用的链接将不起作用(不再)。这是Qestion和答案依赖外部链接而不至少引用相关部分的一般问题。请注意,这违反了网站规则,也是一个有效的否决理由。@Olaf,该链接对我有效。它看起来像是指向G+上用户配置文件的有效链接:
    https://plus.google.com/u/{id}/{id}/posts
    是的,链接指向提出此想法的人的G+个人资料。因此,这与浏览器或地理锁定不兼容。不管是什么,这都违反(当前)网站规则。答案必须是自包含的。答案是自包含的。
    #define is_set(macro) is_set_(macro)
    #define macrotest_1 ,
    #define is_set_(value) is_set__(macrotest_##value)
    #define is_set__(comma) is_set___(comma 1, 0)
    #define is_set___(_, v, ...) v
    
    if (is_set(MACRO)) {
       /* Do something when MACRO is set */
    }