Как проверить, является ли символ препроцессора #define'd, но не имеет значения?

Используя директивы препроцессора C++, возможно ли проверить, определен ли символ препроцессора, но не имеет значения? Что-то вроде того:

#define MYVARIABLE
#if !defined(MYVARIABLE) || #MYVARIABLE == ""
... blablabla ...
#endif

РЕДАКТИРОВАТЬ: причина, почему я делаю это, потому что проект, над которым я работаю, должен взять строку из окружающей среды через /DMYSTR=$(MYENVSTR)и эта строка может быть пустой. Я хочу убедиться, что проект не скомпилируется, если пользователь забыл определить эту строку.

11 ответов

Решение

Сома макро-магия:

#define DO_EXPAND(VAL)  VAL ## 1
#define EXPAND(VAL)     DO_EXPAND(VAL)

#if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1)

Only here if MYVARIABLE is not defined
OR MYVARIABLE is the empty string

#endif

Обратите внимание, что если вы определяете MYVARIABLE в командной строке, значением по умолчанию будет 1

g++ -DMYVARIABLE <file>

Здесь значение MYVARIABLE равно 1

g++ -DMYVARIABLE= <file>

Здесь значение MYVARIABLE является пустой строкой

Проблема цитирования решена:

#define DO_QUOTE(X)        #X
#define QUOTE(X)           DO_QUOTE(X)

#define MY_QUOTED_VAR      QUOTE(MYVARIABLE)

std::string x = MY_QUOTED_VAR;
std::string p = QUOTE(MYVARIABLE);

Я не видел такого решения проблемы, но удивлен, что это не для общего пользования. Вроде работает в Xcode Objc. Различают "определено без значения" и "определено множество 0"

#define TRACE
#if defined(TRACE) && (7-TRACE-7 == 14)
#error TRACE is defined with no value
#endif

Я хочу убедиться, что проект не скомпилируется, если пользователь забыл определить эту строку.

Хотя я проверял это на предыдущем этапе сборки, вы можете сделать это во время компиляции. Использование Boost для краткости:

#define A "a"
#define B
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(A)) > 1); // succeeds
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(B)) > 1); // fails

Ответ Мехрада должен быть расширен, чтобы заставить его работать. Также его комментарий

/* MYVARI(A)BLE здесь не определено */

не является правильным; чтобы проверить неопределенную переменную, есть простой тест #ifndef MYVARIABLE,

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

#ifndef MYVARIABLE
    /* MYVARIABLE is undefined here */
#elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
    /* MYVARIBLE is defined with no value here */
#else
    /* MYVARIBLE is defined here */
#endif

#elif заявление ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1 работает следующим образом:

  • когда MYVARIABLE определяется, но пустой, он расширяется до ~(~+0) == 0 && ~(~+1) == 1, который работает 0==0 && 1==1 (двойное отрицание ~~ - тождественный оператор).
  • когда MYVARIABLE определяется как числовое значение, скажем n, оно расширяется до ~(~n+0)==0 && ~(~n+1)==1, На левой стороне &&, выражение ~(~n+0)==0 оценивает n==0, Но с n==0правая сторона оценивает ~(~0+1)==1с ~0 от -1 до ~(-1+1)==1, затем ~0==1 и наконец -1==1что, очевидно, является ложным.
  • когда MYVARIABLE определяется как нечисловое значение, прекомпилятор уменьшает все неизвестные символы до 0, и мы получаем предыдущий случай с n==0 еще раз.

Мой полный тестовый код (сохранить как файл test.c):

#include <stdio.h>

int main() {
    printf("MYVARIABLE is "
#ifndef MYVARIABLE
     "undefined"
#elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
     "defined without a value"
#else 
     "defined with this value : %i", MYVARIABLE
#endif
    );
    printf("\n");
}

С препроцессором GNU cpp вы можете поэкспериментировать, чтобы увидеть, какой код создается:

# undefined
cpp test.c
#defined without a value
cpp -DMYVARIALBE= test.c
#defined wit an implicit value 1
cpp -DMYVARIALBE test.c
#defined wit an explicit value 1
cpp -DMYVARIALBE=1 test.c
#defined wit an explicit value a
cpp -DMYVARIALBE=a test.c

или вывод компиляции и исполнения (под некоторым linux)

$ gcc -o test test.c ; ./test
MYVARIABLE is undefined
$ gcc -DMYVARIABLE= -o test test.c ; ./test
MYVARIABLE is defined without a value
$ gcc -DMYVARIABLE -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=1 -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=a -o test test.c ; ./test
test.c: In function ‘main’:
<command-line>:0:12: error: ‘a’ undeclared (first use in this function)
...

В последнем запуске, где MYVARIABLE определен как 'a', ошибка не является ошибкой в ​​определении макроса; макрос корректно ведет к последнему случаю, "определенному с этим значением...". Но если это значение "a", а "a" не определено в коде, компилятор или курс должны сигнализировать об этом.

Таким образом, последний случай является очень хорошим примером того, почему цель исходного вопроса очень опасна: с помощью макроса пользователь может ввести любую последовательность программных строк в код для компиляции. Проверка того, что такой код не введен, требует намного больше проверки макроса на допустимые значения. Вероятно, необходим полный сценарий, вместо того, чтобы оставить эту задачу для предварительной обработки. И в таком случае, какая польза от проверки в предварительной обработке?

Вы можете использовать BOOST_PP_IS_EMPTY макрос вроде так:

#include <boost/preprocessor/facilities/is_empty.hpp>

#define MYVARIABLE
#if !defined(MYVARIABLE) || !BOOST_PP_IS_EMPTY(MYVARIABLE)
    // ... blablabla ...
#endif

Это помогло мне. Я добавлю этот макрос без документов, поэтому используйте его с осторожностью.

Источник: препроцессор отсутствует-IS-EMPTY-документация

Вы не можете, так как препроцессор может проверять только числовое значение. Ваше сравнение строк не охватывается синтаксисом препроцессора.

Я не думаю, что это можно сделать. При этом я не вижу в этом необходимости. Когда вы делаете препроцессор #define символ, вы должны установить соглашение, которое вы определяете как 1 или 0 для использования в #ifили оставьте это поле пустым.

Для целочисленных макросов...

Вы можете использовать хак без лишних макросов:

#if ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
/* MYVARIBLE is undefined here */
#endif

В связи с этим, часто мы хотим проверить, является ли макрос неопределенным , нулевым или имеет значение, отличное от нуля :

      #if ! defined(MYVARIABLE)
  // UNDEFINED:  MYVARIABLE is undefined
#elif ~ MYVARIABLE + 1
  // TRUE:       MYVARIABLE is defined and is empty or nonzero
#else
  // FALSE:      MYVARIABLE is defined and is zero
#endif

Как это работает?

  • если пусто, то ~ + 1 оценивается как ненулевое (истина)
  • если не ноль (например, 1) тогда ~ 1 + 1 оценивается как ненулевое (истина)
  • если равно нулю, то ~ 0 + 1 оценивается как ноль (ложь)

Этот метод работает для любого целочисленного значения. Когда это имя другого макроса, значение этого макроса будет использоваться, как и следовало ожидать.

Обратите внимание: если вы просто хотите проверить, MYVARIABLE когда включен флаг времени компиляции (определенный и пустой или ненулевой):

      #if defined(MYVARIABLE) && ~ MYVARIABLE + 1
  // the MYVARIABLE feature is enabled
#endif

#if MYVARIABLE==0Мой ответ должен быть не менее 30 символов, так что должны это сделать!

Предупреждение! Грязный взлом:

      int xxx[1/(sizeof(PASSWORD)-1)];

Взорвется, если пароль не был определен (в моем случае получен из переменной среды, которую необходимо определить извне)

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