Мне нужно сделать что-то подобное в C. Это работает, только если я использую char, но мне нужна строка. Как я могу это сделать?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
c
conditional
c-preprocessor
frx08
источник
источник
Ответы:
Я не думаю, что есть способ полностью выполнять сравнение строк переменной длины в директивах препроцессора. Возможно, вы могли бы сделать следующее:
#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.
источник
#define USER_VS (3 - USER)
в этом конкретном случае. :)[ОБНОВЛЕНИЕ: 2018.05.03]
ПРЕДОСТЕРЕЖЕНИЕ : не все компиляторы реализуют спецификацию C ++ 11 одинаково. Приведенный ниже код работает в компиляторе, который я тестировал, в то время как многие комментаторы использовали другой компилятор.
Цитата из ответа Шафика Ягмура по адресу: Вычисление длины строки C во время компиляции. Это действительно constexpr?
Это слово
can
имеет значение в этом мире.Итак, YMMV на этот (или любой) ответ
constexpr
, в зависимости от интерпретации спецификации компилятором авторами.[ОБНОВЛЕНО 2016.01.31]
Поскольку некоторым не понравился мой предыдущий ответ, потому что он избегал всего
compile time string compare
аспекта OP, выполнив задачу без необходимости сравнения строк, вот более подробный ответ.Вы не можете! Ни в C98, ни в C99. Даже в C11. Никакие манипуляции с МАКРОМ этого не изменят.
Определение
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)
обращения, который преобразует его value (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, чтобы исправить опечатку.]
источник
#if 0 == c_strcmp( USER, JACK )
наconstexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
Ваш пример работает только потому, что USER - это JACK. Если бы ПОЛЬЗОВАТЕЛЬ был КОРОЛЕВОЙ, он бы сказалUSER IS QUEEN
иUSER_VS IS QUEEN
constexpr
) из директив препроцессора.Следующее сработало для меня с лязгом. Разрешает то, что выглядит как сравнение символьных макросов. #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
источник
Используйте числовые значения вместо строк.
Наконец, чтобы преобразовать константы JACK или QUEEN в строку, используйте операторы преобразования строки (и / или токенизации).
источник
Как уже было сказано выше, препроцессор ISO-C11 не поддерживает сравнение строк. Однако проблема присвоения макросу «противоположного значения» может быть решена с помощью «вставки токена» и «доступа к таблице». Простое макрорешение для конкатенации / строкового преобразования Джесси терпит неудачу с 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
), которую намного проще поддерживать, чем конструкция оператора if-then-else, принадлежит Джесси.Наконец, следующий четырехстрочный блок вызывает макросы функционального стиля. Последний четырехстрочный блок взят из ответа Джесси.
Сохранение кода
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
макрос не раскрывается перед преобразованием в строку.источник
#if (S(USER)=="jack")
- Я получаю ошибку препроцессора при использовании"
-error: invalid token at start of a preprocessor expression
.Если ваши строки являются константами времени компиляции (как в вашем случае), вы можете использовать следующий трюк:
#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, поэтому примите это во внимание ...
источник
$
какое-то расширение препроцессора?Ответ Патрика и Джесси Чизхолма заставил меня сделать следующее:
#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
тоже должен работать, но не тестировалсятакже работает, и с ним, возможно, будет проще обращаться.РЕДАКТИРОВАТЬ: Согласно комментарию @ Jean-François Fabre, я адаптировал свой ответ.
источник
(s==QUEEN?1:0)
от(s==QUEEN)
вас не нужно троичной, результат уже булево#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
это в основном статический массив символов фиксированной длины, инициализированный вручную вместо статического массива символов переменной длины, инициализируемый автоматически, всегда заканчивающийся завершающим нулевым символом
источник
Вы не можете этого сделать, если USER определен как строка в кавычках.
Но вы можете это сделать, если ПОЛЬЗОВАТЕЛЬ - это просто ДЖЕК, КОРОЛЕВА, Джокер или что-то еще.
Есть два приема:
#define JACK
к чему-либо.Итак, начнем с:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
Теперь, если я напишу
JACK_QUEEN_OTHER(USER)
, а USER будет JACK, препроцессор превратит это вEXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
Шаг второй - конкатенация:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
Теперь
JACK_QUEEN_OTHER(USER)
становитсяEXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
Это дает возможность добавить несколько запятых в зависимости от того, соответствует ли строка:
#define ReSeRvEd_JACK x,x,x #define ReSeRvEd_QUEEN x,x
Если ПОЛЬЗОВАТЕЛЬ - ДЖЕК,
JACK_QUEEN_OTHER(USER)
становитсяEXPANSION2(x,x,x, 1, 2, 3)
Если ПОЛЬЗОВАТЕЛЬ - КОРОЛЕВА,
JACK_QUEEN_OTHER(USER)
становитсяEXPANSION2(x,x, 1, 2, 3)
Если USER другой,
JACK_QUEEN_OTHER(USER)
становитсяEXPANSION2(ReSeRvEd_other, 1, 2, 3)
В этот момент произошло нечто критическое: четвертый аргумент макроса EXPANSION2 равен 1, 2 или 3, в зависимости от того, был ли передан исходный аргумент валетом, дамой или чем-то еще. Так что все, что нам нужно сделать, это выбрать его. По многим причинам нам понадобятся два макроса для последнего шага; это будут EXPANSION2 и EXPANSION3, даже если один кажется ненужным.
Собирая все вместе, у нас есть эти 6 макросов:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3) #define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e) #define EXPANSION2(a, b, c, d, ...) EXPANSION3(a, b, c, d) #define EXPANSION3(a, b, c, d, ...) d #define ReSeRvEd_JACK x,x,x #define ReSeRvEd_QUEEN x,x
И вы можете использовать их так:
int main() { #if JACK_QUEEN_OTHER(USER) == 1 printf("Hello, Jack!\n"); #endif #if JACK_QUEEN_OTHER(USER) == 2 printf("Hello, Queen!\n"); #endif #if JACK_QUEEN_OTHER(USER) == 3 printf("Hello, who are you?\n"); #endif }
Обязательная ссылка на Godbolt: https://godbolt.org/z/8WGa19
источник
Я думаю, это просто, ты можешь просто сказать
#define NAME JACK #if NAME == queen
источник