Как сравнить строки в условных препроцессорах 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