Использование определенного (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;
}
Поэтому у вас, вероятно, не будет проблем с производительностью, в зависимости от вашего компилятора и любых оптимизаций...