Как сравнить строки в условных препроцессорах C
Мне нужно сделать что-то подобное в C. Это работает, только если я использую char, но мне нужна строка. Как я могу это сделать?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
Ответы
Ответ 1
Я не думаю, что есть способ полностью выполнить сравнения строк длины в директивах препроцессора. Возможно, вы могли бы сделать следующее:
#define USER_JACK 1
#define USER_QUEEN 2
#define USER USER_JACK
#if USER == USER_JACK
#define USER_VS USER_QUEEN
#elif USER == USER_QUEEN
#define USER_VS USER_JACK
#endif
Или вы можете немного реорганизовать код и вместо этого использовать C-код.
Ответ 2
[ОБНОВЛЕНИЕ: 2018.05.03]
ПРЕДУПРЕЖДЕНИЕ. Не все компиляторы реализуют спецификацию C++ 11 одинаково. Приведенный ниже код работает в компиляторе, на котором я тестировал, в то время как многие комментаторы использовали другой компилятор.
Цитата из ответа Шафика Ягмура по адресу: Вычисление длины строки C во время компиляции. Это действительно constexpr?
Не гарантируется, что константные выражения будут оцениваться во время компиляции, у нас есть только ненормативная цитата из черновика стандартного раздела C++ 5.19. Постоянные выражения, которые говорят это, хотя:
[...]> [Примечание: константные выражения могут быть оценены во время перевода.
Это слово can
все значение в мире.
Итак, YMMV в этом (или любом) ответе с участием constexpr
, в зависимости от интерпретации спецификацией автора компилятора.
[ОБНОВЛЕНО 2016.01.31]
Поскольку некоторым не понравился мой предыдущий ответ, потому что он избежал аспекта compile time string compare
, выполнив задачу без необходимости сравнения строк, вот более подробный ответ.
Ты не можешь! Не в C98 или C99. Даже в С11. Никакое количество манипуляций с MACRO не изменит это.
Определение const-expression
используемое в #if
, не допускает строки.
Он допускает символы, поэтому если вы ограничиваете себя символами, вы можете использовать это:
#define JACK 'J'
#define QUEEN 'Q'
#define CHOICE JACK // or QUEEN, your choice
#if 'J' == CHOICE
#define USER "jack"
#define USER_VS "queen"
#elif 'Q' == CHOICE
#define USER "queen"
#define USER_VS "jack"
#else
#define USER "anonymous1"
#define USER_VS "anonymous2"
#endif
#pragma message "USER IS " USER
#pragma message "USER_VS IS " USER_VS
Вы можете! В C++ 11. Если вы определите вспомогательную функцию времени компиляции для сравнения.
// compares two strings in compile time constant fashion
constexpr int c_strcmp( char const* lhs, char const* rhs )
{
return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0
: (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0])
: c_strcmp( lhs+1, rhs+1 );
}
// some compilers may require ((int)lhs[0] - (int)rhs[0])
#define JACK "jack"
#define QUEEN "queen"
#define USER JACK // or QUEEN, your choice
#if 0 == c_strcmp( USER, JACK )
#define USER_VS QUEEN
#elif 0 == c_strcmp( USER, QUEEN )
#define USER_VS JACK
#else
#define USER_VS "unknown"
#endif
#pragma message "USER IS " USER
#pragma message "USER_VS IS " USER_VS
Таким образом, в конечном итоге вам придется изменить способ достижения конечных строковых значений для USER
и USER_VS
.
Вы не можете сделать сравнение строк времени компиляции в C99, но вы можете сделать выбор строк времени компиляции.
Если вам действительно необходимо выполнить сравнение времени компиляции, вам нужно перейти на C++ 11 или более новые варианты, которые позволяют эту функцию.
[ОРИГИНАЛЬНЫЙ ОТВЕТ СЛЕДУЕТ)
Пытаться:
#define jack_VS queen
#define queen_VS jack
#define USER jack // jack or queen, your choice
#define USER_VS USER##_VS // jack_VS or queen_VS
// stringify usage: S(USER) or S(USER_VS) when you need the string form.
#define S(U) S_(U)
#define S_(U) #U
ОБНОВЛЕНИЕ: вставка токена ANSI иногда менее очевидна. ; -D
Помещение одиночного #
перед макросом приводит к тому, что он заменяется строкой своего значения, а не его пустым значением.
Помещение двойного ##
между двумя токенами приводит к их объединению в один токен.
Итак, макрос USER_VS
имеет расширение jack_VS
или queen_VS
, в зависимости от того, как вы установили USER
.
Макрос stringify S(...)
использует косвенную макрокоманду, поэтому значение указанного макроса преобразуется в строку. вместо имени макроса.
Таким образом, USER##_VS
становится jack_VS
(или queen_VS
), в зависимости от того, как вы установили USER
.
Позже, когда stringify макрокоманда используется как S(USER_VS)
величины USER_VS
(jack_VS
в этом примере) передается на этап косвенность S_(jack_VS)
, который преобразует его значение (queen
) в строку "queen"
.
Если вы установите для USER
значение queen
то конечным результатом будет строка "jack"
.
Для объединения токенов см.: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html.
Для преобразования строки токена см.: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
[ОБНОВЛЕНО 2015.02.15 для исправления опечатки.]
Ответ 3
Используйте числовые значения вместо строк.
Наконец, чтобы преобразовать константы JACK или QUEEN в строку, используйте операторы stringize (и/или tokenize).
Ответ 4
Следующее работало для меня с clang. Позволяет отображать символическое сравнение макросов. #error xxx - это просто посмотреть, что делает компилятор. Замена определения cat с помощью #define cat (a, b) a ## b ломает вещи.
#define cat(a,...) cat_impl(a, __VA_ARGS__)
#define cat_impl(a,...) a ## __VA_ARGS__
#define xUSER_jack 0
#define xUSER_queen 1
#define USER_VAL cat(xUSER_,USER)
#define USER jack // jack or queen
#if USER_VAL==xUSER_jack
#error USER=jack
#define USER_VS "queen"
#elif USER_VAL==xUSER_queen
#error USER=queen
#define USER_VS "jack"
#endif
Ответ 5
Ответ Патрика и Джесси Чисхолма заставил меня сделать следующее:
#define QUEEN 'Q'
#define JACK 'J'
#define CHECK_QUEEN(s) (s==QUEEN)
#define CHECK_JACK(s) (s==JACK)
#define USER 'Q'
[... later on in code ...]
#if CHECK_QUEEN(USER)
compile_queen_func();
#elif CHECK_JACK(USER)
compile_jack_func();
#elif
#error "unknown user"
#endif
Вместо #define USER 'Q'
#define USER QUEEN
также должен работать, но не был проверен также работает и может быть проще в обращении.
РЕДАКТИРОВАТЬ: Согласно комментарию @Жан-Франсуа Фабр, я адаптировал свой ответ.
Ответ 6
Как уже было сказано выше, препроцессор ISO-C11 поддерживает не сравнение строк. Тем не менее, проблема присвоения макроса с "противоположным значением" может быть решена с помощью "прошивки маркеров" и "доступа к таблице". Jesses simple concatenate/stringify macro-solution терпит неудачу с gcc 5.4.0, потому что строение выполняется перед оценкой конкатенации (соответствующей ISO C11). Однако это можно исправить:
#define P_(user) user ## _VS
#define VS(user) P_ (user)
#define S(U) S_(U)
#define S_(U) #U
#define jack_VS queen
#define queen_VS jack
S (VS (jack))
S (jack)
S (VS (queen))
S (queen)
#define USER jack // jack or queen, your choice
#define USER_VS USER##_VS // jack_VS or queen_VS
S (USER)
S (USER_VS)
Первая строка (макрос P_()
) добавляет одну косвенность, чтобы следующая строка (макрос VS()
) завершила конкатенацию перед строкой (см. Для чего нужен двойной слой косвенности для макросов?. Макросы строения (S()
и S_()
) относятся к Jesse.
Таблица (макросы jack_VS
и queen_VS
), которую намного легче поддерживать, чем конструкция if-then-else OP от Джесси.
Наконец, следующий четырехстрочный блок вызывает макросы в стиле функции. Последний четырехстрочный блок из ответа Джесса.
Сохранение кода в foo.c
и вызов препроцессора gcc -nostdinc -E foo.c
дает:
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"
"jack"
"USER_VS"
Вывод выполняется так, как ожидалось. В последней строке показано, что макрос USER_VS
не расширен до начала строки.
Ответ 7
Если ваши строки являются константами времени компиляции (как в вашем случае), вы можете использовать следующий трюк:
#define USER_JACK strcmp(USER, "jack")
#define USER_QUEEN strcmp(USER, "queen")
#if $USER_JACK == 0
#define USER_VS USER_QUEEN
#elif USER_QUEEN == 0
#define USER_VS USER_JACK
#endif
Компилятор может заранее сообщить результат strcmp и заменит strcmp своим результатом, тем самым предоставив вам #define, который можно сравнить с директивами препроцессора. Я не знаю, есть ли разница между компиляторами/зависимостью от параметров компилятора, но это сработало для меня в GCC 4.7.2.
РЕДАКТИРОВАТЬ: при дальнейших исследованиях, похоже, что это расширение инструментальной цепочки, а не расширение GCC, поэтому учтите это...
Ответ 8
#define USER_IS(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9)\
ch0==c0 && ch1==c1 && ch2==c2 && ch3==c3 && ch4==c4 && ch5==c5 && ch6==c6 && ch7==c7 ;
#define ch0 'j'
#define ch1 'a'
#define ch2 'c'
#define ch3 'k'
#if USER_IS('j','a','c','k',0,0,0,0)
#define USER_VS "queen"
#elif USER_IS('q','u','e','e','n',0,0,0)
#define USER_VS "jack"
#endif
это в основном статический массив символов фиксированной длины, инициализируемый вручную вместо статического массива символов переменной длины, инициализируемый автоматически, всегда заканчивающийся завершающим нулевым символом
Ответ 9
Это просто, я думаю, вы можете просто сказать
#define NAME JACK
#if NAME == queen