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

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

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

EDIT: Причина, по которой я это делаю, заключается в том, что проект, над которым я работаю, должен взять строку из среды через /DMYSTR=$(MYENVSTR), и эта строка может быть пустой. Я хочу убедиться, что проект не скомпилируется, если пользователь забыл определить эту строку.

Ответ 1

Сома магия:

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

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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

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

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

Источник: preprocessor-missing-IS-EMPTY-documentation

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

/* MYVARI (A) BLE undefined здесь */

неверно; для проверки переменной undefined существует простой тест #ifndef MYVARIABLE.

После такого теста, однако, его выражение приводит к правильному решению исходного вопроса. Я тестировал, что этот код работает, для undefined, определенных, но пустых и непустых значений макроса 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" не определено в коде, компилятор или курс должны сигнализировать об этом.

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

Ответ 9

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