Cómo comparar cadenas en C condicional preprocesador-directivas

Tengo que hacer algo como esto en C. Funciona solo si uso un char, pero necesito una cadena. ¿Cómo puedo hacer esto?

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

No creo que haya una forma de hacer comparaciones de cadenas de longitud variable por completo en las directivas de preprocesador. Sin embargo, podrías hacer lo siguiente:

 #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 

O podría refactorizar el código un poco y usar el código C en su lugar.

[ACTUALIZACIÓN: 2018.05.03]

CAVEAT : No todos los comstackdores implementan la especificación C ++ 11 de la misma manera. El código siguiente funciona en el comstackdor que probé, mientras que muchos comentaristas usaron un comstackdor diferente.

Citando la respuesta de Shafik Yaghmour en: Calcular la longitud de una cadena C en tiempo de comstackción. ¿Es esto realmente un constexto?

No se garantiza que las expresiones constantes se evalúen en tiempo de comstackción, solo tenemos una cita no normativa del borrador de la sección estándar de C ++ 5.19 Expresiones constantes que dice esto sin embargo:

[…]> [Nota: las expresiones constantes se pueden evaluar durante la traducción.-nota final]

Esa palabra can marcar la diferencia en el mundo.

Entonces, YMMV en esta (o cualquiera) respuesta que involucra constexpr , dependiendo de la interpretación del comstackdor de la especificación.

[ACTUALIZADO 2016.01.31]

Como a algunos no les gustó mi respuesta anterior porque evitó todo el aspecto de compile time string compare logrando el objective sin necesidad de comparar cadenas, aquí hay una respuesta más detallada.

¡No puedes! No en C98 o C99. Ni siquiera en C11. Ninguna cantidad de manipulación MACRO cambiará esto.

La definición de const-expression usada en el #if no permite cadenas.

Permite caracteres, por lo que si te limitas a los personajes, puedes usar esto:

 #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 

¡Usted puede! En C ++ 11. Si define una función auxiliar de tiempo de comstackción para la comparación.

 // 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 

Por lo tanto, en última instancia, tendrá que cambiar la forma en que logra su objective de elegir los valores de cadena finales para USER y USER_VS .

No se puede comparar la cadena de tiempo de comstackción en C99, pero puede hacer tiempo de comstackción eligiendo cadenas.

Si realmente debe hacer comparaciones de tiempo de comstackción, entonces necesita cambiar a C ++ 11 o variantes más nuevas que permitan esa función.

[RESPUESTA ORIGINAL A CONTINUACIÓN]

Tratar:

 #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 

ACTUALIZACIÓN: pegar token ANSI a veces es menos que obvio. ;-RE

Poner un único # antes de una macro hace que se cambie a una cadena de su valor, en lugar de su valor simple.

Poner un ## doble entre dos tokens hace que se concatenen en un solo token.

Entonces, la macro USER_VS tiene la expansión jack_VS o queen_VS , dependiendo de cómo configure USER .

La macro de stringify S(...) usa macro indirección para que el valor de la macro nombrada se convierta en una cadena. en lugar del nombre de la macro.

Por lo tanto, USER##_VS convierte en jack_VS (o queen_VS ), dependiendo de cómo configure USER .

Más tarde, cuando la macro stringify se usa como S(USER_VS) el valor de USER_VS ( jack_VS en este ejemplo) se pasa al paso indirecto S_(jack_VS) que convierte su valor ( queen ) en una cadena "queen" .

Si configura USER en queen , el resultado final es la cadena "jack" .

Para la concatenación de tokens, consulte: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

Para la conversión de cadenas de tokens, consulte: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

[ACTUALIZADO 2015.02.15 para corregir un error tipográfico]

Use valores numéricos en lugar de cadenas.

Finalmente, para convertir las constantes JACK o QUEEN a una cadena, use los operadores de stringize (y / o tokenize).

Lo siguiente funcionó para mí con clang. Permite lo que aparece como comparación simbólica de valores de macro. #error xxx es solo para ver qué comstackdor realmente hace. Reemplazar la definición de gato con #define cat (a, b) a ## b rompe las cosas.

 #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 

Como ya se indicó anteriormente, el preprocesador ISO-C11 no es compatible con la comparación de cadenas. Sin embargo, el problema de asignar una macro con el “valor opuesto” se puede resolver con “pegar token” y “acceso a la tabla”. La macro solución de concatenación / stringify simple de Jesse falla con gcc 5.4.0 porque la secuenciación se realiza antes de la evaluación de la concatenación (conforme a ISO C11). Sin embargo, se puede arreglar:

 #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) 

La primera línea (macro P_() ) agrega una indirección para permitir que la línea siguiente (macro VS() ) finalice la concatenación antes de la cadena (ver ¿Por qué necesito doble capa de direccionamiento indirecto para las macros? ). Las macros de stringization ( S() y S_() ) son de Jesse.

La tabla (macros jack_VS y queen_VS ) que es mucho más fácil de mantener que la construcción if-then-else del OP es de Jesse.

Finalmente, el siguiente bloque de cuatro líneas invoca las macros de estilo de función. El último bloque de cuatro líneas es de la respuesta de Jesse.

Almacenar el código en foo.c e invocar el preprocesador gcc -nostdinc -E foo.c produce:

 # 1 "foo.c" # 1 "" # 1 "" # 1 "foo.c" # 9 "foo.c" "queen" "jack" "jack" "queen" "jack" "USER_VS" 

La salida es como se esperaba. La última línea muestra que la macro USER_VS no está expandida antes de la stringización.

La respuesta de Patrick y de Jesse Chisholm me hizo hacer lo siguiente:

 #define QUEEN 'Q' #define JACK 'J' #define CHECK_QUEEN(s) (s==QUEEN?1:0) #define CHECK_JACK(s) (s==JACK?1:0) #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 

En lugar de #define USER 'Q' #define USER QUEEN también debería funcionar, pero no fue probado también funciona y podría ser más fácil de manejar.

Si sus cadenas son constantes de tiempo de comstackción (como en su caso) puede usar el siguiente truco:

 #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 

El comstackdor puede decir el resultado del strcmp por adelantado y reemplazará el strcmp con su resultado, por lo tanto, le dará un #define que se puede comparar con las directivas del preprocesador. No sé si hay alguna diferencia entre los comstackdores / dependencia de las opciones del comstackdor, pero me funcionó en GCC 4.7.2.

EDITAR: tras una investigación más profunda, parece que se trata de una extensión de cadena de herramientas, no de extensión GCC, así que tenlo en cuenta …

Es simple, creo que solo puedes decir

 #define NAME JACK #if NAME == queen