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
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
Respuestas:
No creo que haya una manera de hacer comparaciones de cadenas de longitud variable completamente en las directivas del preprocesador. Sin embargo, quizás puedas 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 puede refactorizar un poco el código y usar código C en su lugar.
#define USER_VS (3 - USER)en este caso específico. :)
[ACTUALIZACIÓN: 2018.05.03]
AVISO : No todos los compiladores implementan la especificación C ++ 11 de la misma manera. El siguiente código funciona en el compilador en el que probé, mientras que muchos comentaristas usaron un compilador diferente.
Citando la respuesta de Shafik Yaghmour en: Calcular la longitud de una cadena C en tiempo de compilación. ¿Es esto realmente un constexpr?
No se garantiza que las expresiones constantes se evalúen en tiempo de compilación, solo tenemos una cita no normativa del borrador del estándar C ++ sección 5.19 Expresiones constantes que dice esto:
[...]> [Nota: las expresiones constantes se pueden evaluar durante la traducción. — nota final]
Esa palabra canmarca la diferencia en el mundo.
Entonces, YMMV en esta (o cualquier) respuesta que involucre constexpr, dependiendo de la interpretación de la especificación del escritor del compilador.
[ACTUALIZADO 2016.01.31]
Como a algunos no les gustó mi respuesta anterior porque evitaba todo el compile time string compareaspecto del OP al lograr el objetivo 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-expressionusado en #ifno permite cadenas.
Sí permite caracteres, por lo que si se limita a los caracteres, puede 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 compilació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
Entonces, en última instancia, tendrá que cambiar la forma en que logra su objetivo de elegir valores de cadena finales para USERy USER_VS.
No puede comparar cadenas de tiempo de compilación en C99, pero puede elegir cadenas de tiempo de compilación.
Si realmente debe hacer comparaciones de tiempo de compilación, debe cambiar a C ++ 11 o variantes más nuevas que permitan esa característica.
[SIGUE LA RESPUESTA ORIGINAL]
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: el pegado de tokens ANSI a veces es menos que obvio. ;-RE
Poner un solo #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_VStiene la expansión jack_VSo queen_VS, dependiendo de cómo lo establezca USER.
La macro stringifyS(...) usa macro indirección para que el valor de la macro nombrada se convierta en una cadena. en lugar del nombre de la macro.
Así se USER##_VSconvierte en jack_VS(o queen_VS), dependiendo de cómo lo establezca USER.
Más tarde, cuando se usa la macro stringify , S(USER_VS)el valor de USER_VS( jack_VSen este ejemplo) se pasa al paso de indirección S_(jack_VS)que convierte su valor ( queen) en una cadena "queen".
Si establece USERen 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.]
#if 0 == c_strcmp( USER, JACK )aconstexpr int comp1 = c_strcmp( USER, JACK ); #if 0 == comp1
#ifSu ejemplo solo funciona porque USUARIO es JACK. Si el USUARIO fuera REINA, diría USER IS QUEENyUSER_VS IS QUEEN
constexpr) desde las directivas del preprocesador.
Lo siguiente funcionó para mí con clang. Permite lo que aparece como una comparación de valores macro simbólicos. #error xxx es solo para ver qué hace realmente el compilador. Reemplazar la definición de gato con #define cat (a, b) a ## b rompe 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 admite la comparación de cadenas. Sin embargo, el problema de asignar una macro con el "valor opuesto" se puede resolver con "pegar tokens" y "acceso a la tabla". La macro-solución de concatenar / encadenar simple de Jesse falla con gcc 5.4.0 porque la cadena 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 siguiente línea (macro VS()) termine la concatenación antes de la secuenciación (consulte ¿Por qué necesito una doble capa de indirección para macros? ). Las macros de encadenamiento ( S()y S_()) son de Jesse.
La tabla (macros jack_VSy 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 foo.ce invocar el preprocesador gcc -nostdinc -E foo.cproduce:
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"
"jack"
"USER_VS"
La salida es la esperada. La última línea muestra que la USER_VSmacro no se expande antes de la cadena.
#if (S(USER)=="jack")- Recibo un error de preprocesador cuando uso el "- error: invalid token at start of a preprocessor expression.
Si sus cadenas son constantes de tiempo de compilació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 compilador puede decirle el resultado de strcmp de antemano y reemplazará strcmp con su resultado, dándole así una #define que se puede comparar con las directivas del preprocesador. No sé si hay alguna variación entre los compiladores / dependencia de las opciones del compilador, pero funcionó para mí en GCC 4.7.2.
EDITAR: después de una mayor investigación, parece que esta es una extensión de cadena de herramientas, no una extensión de GCC, así que téngalo en cuenta ...
$algún tipo de extensión de preprocesador?
La respuesta de Patrick y Jesse Chisholm me hizo hacer lo siguiente:
#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
En vez 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.
EDITAR: Según el comentario de @ Jean-François Fabre, adapté mi respuesta.
(s==QUEEN?1:0)por (s==QUEEN)no necesita el ternario, el resultado ya es un booleano
#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
es básicamente una matriz de caracteres estáticos de longitud fija inicializada manualmente en lugar de una matriz de caracteres estáticos de longitud variable inicializada automáticamente y siempre termina con un carácter nulo de terminación
No puede hacer eso si USUARIO se define como una cadena entre comillas.
Pero usted puede hacer eso si el USUARIO es sólo JACK o reina o Joker o lo que sea.
Hay dos trucos para usar:
#define JACKhacerlo.Así que comencemos con:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
Ahora, si escribo JACK_QUEEN_OTHER(USER), y USER es JACK, el preprocesador lo convierte enEXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
El segundo paso es la concatenación:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
Ahora se JACK_QUEEN_OTHER(USER)convierte enEXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
Esto da la oportunidad de agregar una serie de comas según si una cadena coincide o no:
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
Si USUARIO es JACK, se JACK_QUEEN_OTHER(USER)convierte enEXPANSION2(x,x,x, 1, 2, 3)
Si USUARIO es REINA, se JACK_QUEEN_OTHER(USER)convierte enEXPANSION2(x,x, 1, 2, 3)
Si USUARIO es otro, se JACK_QUEEN_OTHER(USER)convierte enEXPANSION2(ReSeRvEd_other, 1, 2, 3)
En este punto, ha sucedido algo crítico: el cuarto argumento de la macro EXPANSION2 es 1, 2 o 3, dependiendo de si el argumento original pasado fue jota, reina o cualquier otra cosa. Así que todo lo que tenemos que hacer es elegirlo. Por razones extensas, necesitaremos dos macros para el último paso; serán EXPANSION2 y EXPANSION3, aunque una parezca innecesaria.
Poniéndolo todo junto, tenemos estas 6 macros:
#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
Y podrías usarlos así:
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
}
Enlace obligatorio de Godbolt: https://godbolt.org/z/8WGa19
Es simple, creo que puedes decir
#define NAME JACK
#if NAME == queen