Есть ли способ проверить, определен ли макрос и равен ли он определенному значению одновременно?
Я регулярно использую объектоподобные макросы препроцессора в качестве логических флагов в коде C для включения и выключения разделов кода.
Например
#define DEBUG_PRINT 1
А потом использовать как
#if(DEBUG_PRINT == 1)
printf("%s", "Testing");
#endif
Однако возникает проблема, если заголовочный файл, содержащий #define
забыто быть включенным в исходный код. Поскольку макрос не объявлен, препроцессор обрабатывает его так, как если бы он равнялся 0, и #if
заявление никогда не работает.
Когда заголовочный файл забыт для включения, может произойти непредвиденное, неуправляемое поведение.
В идеале я хотел бы иметь возможность проверить, что макрос определен, и проверить, что он равен определенному значению в одной строке. Если он не определен, препроцессор выдает ошибку (или предупреждение).
Я ищу что-то вроде:
#if-def-and-true-else-throw-error(DEBUG_PRINT)
...
#endif
Это как комбинация #ifdef
а также #if
и, если он не существует, использует #error
,
Я исследовал несколько путей, однако директивы препроцессора не могут использоваться внутри #define
блок, и, насколько я могу судить, нет никакой препроцессорной опции, чтобы выдавать ошибки / предупреждения, если макрос не определен при использовании внутри #if
заявление.
6 ответов
Это может не сработать для общего случая (я не думаю, что есть общее решение для того, что вы просите), но для вашего конкретного примера вы можете рассмотреть изменение этой последовательности кода:
#if(DEBUG_PRINT == 1)
printf("%s", "Testing");
#endif
чтобы:
if (DEBUG_PRINT == 1) {
printf("%s", "Testing");
}
Это больше не многословно и не сможет скомпилировать, если DEBUG_PRINT
не определен или если он определен как нечто, что нельзя сравнить с 1
,
насколько я могу судить, нет никакой опции препроцессора, чтобы выдавать ошибки / предупреждения, если макрос не определен при использовании внутри
#if
заявление.
Это не может быть ошибкой, потому что стандарт C определяет, что поведение допустимо. Из раздела 6.10.1/3 стандарта ISO C99:
После всех замен из-за расширения макроса и
defined
унарный оператор выполнен, все остальные идентификаторы заменены на номер pp0
....
Как отмечает Джим Балтер в комментарии ниже, некоторые компиляторы (такие как gcc) могут выдавать предупреждения об этом. Тем не менее, так как поведение замены 0
поскольку нераспознанные токены препроцессора являются законными (и во многих случаях желательными), я ожидаю, что включение таких предупреждений на практике приведет к значительному шуму.
Там нет никакого способа сделать именно то, что вы хотите. Если вы хотите сгенерировать ошибку компиляции, если макрос не определен, вам придется сделать это явно
#if !defined DEBUG_PRINT
#error DEBUG_PRINT is not defined.
#endif
для каждого исходного файла, который заботится. Кроме того, вы можете преобразовать свой макрос в функциональный макрос и избежать использования #if
, Например, вы можете определить DEBUG_PRINT
макрос, который расширяется до printf
вызов для отладочных сборок, но расширяется до ничего для не отладочных сборок. Любой файл, который игнорирует включение заголовка, определяющего макрос, не сможет скомпилироваться.
Да, вы можете проверить оба:
#if defined DEBUG && DEBUG == 1
# define D(...) printf(__VA_ARGS__)
#else
# define D(...)
#endif
В этом примере, даже когда #define DEBUG 0
но он не равен 1, поэтому ничего не будет напечатано.
Вы можете сделать даже это:
#if defined DEBUG && DEBUG
# define D(...) printf(__VA_ARGS__)
#else
# define D(...)
#endif
Здесь, если вы #define DEBUG 0
а потом D(1,2,3)
также ничего не будет напечатано
Вместо того, чтобы использовать DEBUG_PRINT непосредственно в ваших исходных файлах, поместите это в заголовочный файл:
#if !defined(DEBUG_PRINT)
#error DEBUG_PRINT is not defined
#endif
#if DEBUG_PRINT
#define PrintDebug([args]) [definition]
#else
#define PrintDebug
#endif
Любой исходный файл, который использует PrintDebug, но не включает файл заголовка, не сможет скомпилироваться.
Если вам нужен код, отличный от вызовов PrintDebug, для компиляции на основе DEBUG_PRINT, рассмотрите возможность использования предложением Майкла Барра об использовании обычного if
скорее, чем #if
(да, оптимизатор не будет генерировать код в тесте с ложной константой).
Изменить: И вы можете обобщить PrintDebug выше, чтобы включить или исключить произвольный код, если у вас нет запятых, которые похожи на аргументы макроса:
#if !defined(IF_DEBUG)
#error IF_DEBUG is not defined
#endif
#if IF_DEBUG
#define IfDebug(code) code
#else
#define IfDebug(code)
#endif
Тогда вы можете написать что-то вроде
IfDebug(int count1;) // IfDebug(int count1, count2;) won't work
IfDebug(int count2;)
...
IfDebug(count1++; count2++;)
#if 0 // 0/1
#define DEBUG_PRINT printf("%s", "Testing")
#else
#define DEBUG_PRINT printf("%s")
#endif
Поэтому, когда "если 0", то ничего не будет делать, а когда "если 1", он будет выполнять определенный макрос.
Просто создайте макрос DEBUG_PRINT, который выполняет фактическую печать:
#define DEBUG_PRINT(n, str) \
\
if(n == 1) \
{ \
printf("%s", str); \
} \
else if(n == 2) \
{ \
do_something_else(); \
} \
\
#endif
#include <stdio.h>
int main()
{
DEBUG_PRINT(1, "testing");
}
Если макрос не определен, вы получите ошибку компилятора, потому что символ не распознан.