Препроцессор C: разверните макрос в предупреждении

Я хотел бы напечатать значение макроса (разверните макрос) в директиве #warning.

Например, для кода:

#define AAA 17
#warning AAA = ???

Желаемый вывод времени компиляции будет

warning: AAA = 17

Что я использую для??? или как дополнить код?

5 ответов

Решение

Вы можете использовать директиву препроцессора #pragma message,

Пример:

#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

#define AAA 123
#pragma message "content of AAA: " STR(AAA)

int main() { return 0; }

Вывод может выглядеть так:

$ gcc test.c
test.c:5:9: note: #pragma message: content of AAA: 123
 #pragma message("content of AAA: " STR(AAA))
         ^

Для справки:

Если вы действительно хотите выдать предупреждение, сработает также следующее. Однако это зависит от того, включен ли C99 (работает с gcc 4.8.2 или новее, не тестировался в более ранних версиях):

#define N 77

#define __STRINGIFY(TEXT) #TEXT
#define __WARNING(TEXT) __STRINGIFY(GCC warning TEXT)
#define WARNING(VALUE) __WARNING(__STRINGIFY(N = VALUE))

#if N == 77
_Pragma (WARNING(N))
#endif

Я бы не рекомендовал использовать #warning, так как это не стандартная версия C. Кроме того, что вы хотите предупредить, но не выдать ошибку? Предупреждения - это, как правило, то, что компилятор использует, когда вы делаете что-то подозрительное, что мы опасно, но допускается стандартом C. У вас нет такого случая в обычном приложении, вы захотите, чтобы оно компилировалось без ошибок или вообще не компилировалось. Поэтому я бы использовал стандартный #error, а не нестандартный #warning.

Вы не можете ввести фактическое содержание определения препроцессора. Что-то вроде этого может быть достаточно:

#if (AAA < SOMETHING) && (AAA > SOMETHING_ELSE)
  #error AAA is bad.
#endif

Я думаю, что это достаточно подробно для программиста. Однако, если вы действительно хотите больше подробностей и у вас есть современный компилятор C, вы можете использовать static_assert. Тогда вы можете достичь чего-то близкого к тому, что вы хотите:

#include <assert.h>

#define xstr(s) str(s)
#define str(s) #s
#define err_msg(x) #x " is " xstr(x)

#define AAA 17

static_assert(AAA != 17, err_msg(AAA));

этот макрос беспорядок должен печатать AAA 17. Объяснение того, как работают эти макросы, можно найти здесь.

Я не уверен, был ли static_assert включен в C99 или C11, это, безусловно, в C11. Возможно, вам придется использовать какое-то расширение GCC, чтобы включить его.

Много раз у меня Makefile генерировал локальный сгенерированный файл.h, который содержит нужные определения.

Генерируемый.h:  Makefile
        echo >generate.h "// ПРЕДУПРЕЖДЕНИЕ: сгенерированный файл. Вместо этого измените Makefile"
        дата >> сгенерировано.h '+// сгенерировано на%Y-%m-%d %H:%M:%S'
        echo >>generate.h "#if AAA == AAA_bad"
        echo >>generate.h "#warning \"AAA = $(AAA_bad)\""
        echo >>generate.h "#endif"

Необходимость в #include "generate.h" очевидна.

Естественно, вы можете вращать здесь любую сложность, но если она получает больше, чем несколько строк, вы можете поместить сложность в отдельный скрипт, так как загроможденные Make-файлы могут быть проблемой обслуживания. С небольшим воображением у вас могут быть циклы, генерирующие большое количество тестов из небольшого ввода.

Наличие цели target.h, зависящей от Makefile, имеет решающее значение для гарантии того, что генерируемый файл переделывается, если инструкции в цели меняются. Если у вас есть отдельный сгенерированный скрипт.sh, он тоже будет в списке зависимостей.

Отказ от ответственности: не проверено на реальные.

Еще один простой метод, особенно когда вы имеете дело с проектом Makefile (например, linux, u-boot, qemu, ..), вы можете увидеть предварительно обработанный результат файла.
Например,

чтобы увидеть предварительно обработанный файл arch/arm64/kernel/head.S,
вы можете сделать arch/arm64/kernel/head.s. (маленькая с).

чтобы увидеть предварительно обработанный файл foo/bar/baz.c,
вы можете сделать foo/bar/baz.i

Все макросы расширены до конечного значения.

Другие вопросы по тегам