Как проверить, является ли символ препроцессора # define'd, но не имеет значения? - PullRequest
33 голосов
/ 23 сентября 2010

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

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

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

Ответы [ 9 ]

38 голосов
/ 23 сентября 2010

Макро-магия сомы:

#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);
10 голосов
/ 23 сентября 2010

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

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

#define A "a"
#define B
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(A)) > 1); // succeeds
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(B)) > 1); // fails
9 голосов
/ 07 ноября 2012

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

#define TRACE
#if defined(TRACE) && (7-TRACE-7 == 14)
#error TRACE is defined with no value
#endif
5 голосов
/ 08 февраля 2018

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

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

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

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

#ifndef MYVARIABLE
    /* MYVARIABLE is undefined here */
#elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
    /* MYVARIABLE is defined with no value here */
#else
    /* MYVARIABLE 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 -DMYVARIABLE= test.c
#defined wit an implicit value 1
cpp -DMYVARIABLE test.c
#defined wit an explicit value 1
cpp -DMYVARIABLE=1 test.c
#defined wit an explicit value a
cpp -DMYVARIABLE=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» не определено в коде, компилятор или курс должны сигнализировать об этом.

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

3 голосов
/ 13 марта 2013

Вы можете использовать макрос 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-Документация

2 голосов
/ 23 сентября 2010

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

2 голосов
/ 23 сентября 2010

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

0 голосов
/ 22 марта 2018

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

0 голосов
/ 21 января 2018

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

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

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