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
c
conditional
c-preprocessor
frx08
fuente
fuente
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.
fuente
#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?
Esa palabra
can
marca 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 compare
aspecto 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-expression
usado en#if
no 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
USER
yUSER_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_VS
tiene la expansiónjack_VS
oqueen_VS
, dependiendo de cómo lo establezcaUSER
.La macro 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.Así se
USER##_VS
convierte enjack_VS
(oqueen_VS
), dependiendo de cómo lo establezcaUSER
.Más tarde, cuando se usa la macro stringify ,
S(USER_VS)
el valor deUSER_VS
(jack_VS
en este ejemplo) se pasa al paso de indirecciónS_(jack_VS)
que convierte su valor (queen
) en una cadena"queen"
.Si establece
USER
enqueen
, 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.]
fuente
#if 0 == c_strcmp( USER, JACK )
aconstexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
Su ejemplo solo funciona porque USUARIO es JACK. Si el USUARIO fuera REINA, diríaUSER IS QUEEN
yUSER_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
fuente
Utilice valores numéricos en lugar de cadenas.
Finalmente, para convertir las constantes JACK o QUEEN en una cadena, use los operadores de cadena (y / o tokenización).
fuente
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 (macroVS()
) 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()
yS_()
) son de Jesse.La tabla (macros
jack_VS
yqueen_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.c
e invocar el preprocesadorgcc -nostdinc -E foo.c
produce:# 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_VS
macro no se expande antes de la cadena.fuente
#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 ...
fuente
$
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 probadotambié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.
fuente
(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
fuente
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 JACK
hacerlo.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
fuente
Es simple, creo que puedes decir
#define NAME JACK #if NAME == queen
fuente