Использование определенного (MACRO) внутри оператора C if

Я хотел бы написать код в C примерно так:

if(defined(MACRO))
  ...
else
  ...

но я не мог найти никакого способа сделать это в C, так как указанный препроцессор (MACRO) работает только внутри # if. Есть ли способ сделать это?

Мне очень нравится писать:

ASSERT(UART, var >= 0);

где

#define ASSERT(NAME, TEST) \
  do { \
    if (defined(NAME) && !(TEST)) \
      printf("Assert failed"); \
  } while(0)

таким образом, я мог бы включить проверки ASSERT, когда макрос определен и если он не определен, то утверждения не должны проверяться. Если вы попытаетесь это сделать, вы получите:

implicit declaration of function `defined'

что вполне понятно, поскольку компилятор GCC не находит оператор препроцессора defined().

Ответ 1

Макрос comex расширяется до 1, если аргумент определен равным 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 */
}

Ответ 2

Хорошо, основываясь на предыдущем сообщении, я получил эту идею, которая, похоже, работает:

#define DEFINEDX(NAME) ((#NAME)[0] == 0)
#define DEFINED(NAME) DEFINEDX(NAME)

Это будет проверять, определено ли NAME, и поэтому оно расширяется до пустой строки с 0 в ее первом символе, или это undefined, и в этом случае это не пустая строка. Это работает с GCC, поэтому можно написать

if( DEFINED(MACRO) )
  ...

Ответ 3

Почему бы вам просто не определить ASSERT по-разному в зависимости от этого макроса?

#ifdef MACRO
#define ASSERT(NAME, TEST) \
    do { \
        printf("Assert failed"); \
    } while(0)
#else
#define ASSERT(NAME, TEST) {}
#endif

Следует избегать использования фиксированных значений препроцессора в условиях C - убедитесь, что компилятор должен оптимизировать код мертвого кода, но зачем полагаться на это, когда вы можете существенно удалить фактический код C?

EDIT:

Существует довольно уродливый трюк, связанный с строкой строкового макроса, который вы можете использовать:

#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;
}

Выводится:

$ ./test
Error: X

По сути, он использует тот факт, что, когда токен препроцессора не определяется как макрос, он расширяется сам по себе. Когда, с другой стороны, определяется, что он расширяется до пустой строки или ее определения. Если один из ваших макросов имеет свое собственное имя в качестве определения, этот хак должен работать.

Отказ от ответственности: используйте это на свой страх и риск!

(... потому что я, конечно же, не буду его использовать!)

ИЗМЕНИТЬ 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 уменьшает эту программу до одного вызова puts(). Эта программа производит точно такой же сборный вывод:

#include <stdio.h>

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

    return 0;
}

Поэтому у вас, вероятно, не будет проблем с производительностью, в зависимости от вашего компилятора и любых оптимизаций...