Как сравнивать строки в условных директивах препроцессора C - PullRequest
77 голосов
/ 25 февраля 2010

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

#define USER "jack" // jack or queen

#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif

Ответы [ 9 ]

58 голосов
/ 25 февраля 2010

Я не думаю, что есть способ полностью выполнить сравнение строк переменной длины в директивах препроцессора. Возможно, вы могли бы сделать следующее:

#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

Или вы можете немного изменить код и использовать вместо этого код на Си.

20 голосов
/ 20 мая 2014

[ОБНОВЛЕНИЕ: 2018.05.03]

CAVEAT : Не все компиляторы реализуют спецификацию 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

Можно! В С ++ 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

UPDATE: Вставка токена ANSI иногда менее очевидна. ; -D

Помещение одиночного # перед макросом приводит к тому, что он заменяется строкой своего значения, а не его пустым значением.

Помещение двойного ## между двумя токенами приводит к их объединению в один токен.

Итак, макрос USER_VS имеет расширение jack_VS или queen_VS, в зависимости от того, как вы установите USER.

stringify macro 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 для исправления опечатки.]

7 голосов
/ 25 февраля 2010

Используйте числовые значения вместо строк.

Наконец, чтобы преобразовать константы JACK или QUEEN в строку, используйте операторы stringize (и / или tokenize).

3 голосов
/ 02 августа 2017

Следующее работало у меня с 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
1 голос
/ 09 мая 2017

Как уже говорилось выше, препроцессор ISO-C11 не поддерживает сравнение строк. Однако проблему назначения макроса с «противоположным значением» можно решить с помощью «вставки токена» и «доступа к таблице». Простое макрос-решение Jesse для конкатенации / stringify завершается неудачно с 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_()) взяты из Джесси.

Таблица (макросы jack_VS и queen_VS), которую гораздо проще поддерживать, чем конструкция 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 является , а не , развернутым до преобразования в строку.

1 голос
/ 17 сентября 2016

Ответ от Патрика и Джесси Чисхолма заставил меня сделать следующее:

#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 также должен работать, но не был проверен также работает и может быть проще в обращении.

РЕДАКТИРОВАТЬ: Согласно комментарию @ Жан-Франсуа Фабр, я адаптировал свой ответ.

0 голосов
/ 18 сентября 2018
#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

это в основном статический массив фиксированной длины, инициализированный вручную вместо статического массива char переменной длины, инициализируемого автоматически, всегда заканчивающегося завершающим нулевым символом

0 голосов
/ 28 июля 2017

Это просто, я думаю, вы можете просто сказать

#define NAME JACK    
#if NAME == queen 
0 голосов
/ 10 февраля 2013

Если ваши строки являются константами времени компиляции (как в вашем случае), вы можете использовать следующий прием:

#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, так что примите это во внимание ...

...