Come confrontare stringhe in direttive condizionali del preprocessore C

Devo fare qualcosa di simile in C. Funziona solo se uso un carattere, ma ho bisogno di una stringa. Come posso fare questo?

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

Non penso che ci sia un modo per fare confronti di stringhe di lunghezza variabile completamente nelle direttive del preprocessore. Potresti forse fare ciò che segue:

 #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 

Oppure puoi rifattare il codice un po ‘e usare invece il codice C.

[AGGIORNAMENTO: 2018.05.03]

CAVEAT : non tutti i compilatori implementano le specifiche C ++ 11 allo stesso modo. Il codice seguente funziona nel compilatore su cui ho provato, mentre molti commentatori hanno utilizzato un compilatore diverso.

Citando la risposta di Shafik Yaghmour a: Calcolare la lunghezza di una stringa C in fase di compilazione. È davvero un constexpr?

Le espressioni costanti non sono garantite per essere valutate in fase di compilazione, abbiamo solo una citazione non normativa dalla bozza della sezione standard C ++ 5.19 espressioni costanti che dice questo però:

[…]> [Nota: le espressioni costanti possono essere valutate durante la traduzione.-nota finale]

Quella parola can fare la differenza nel mondo.

Quindi, YMMV su questa (o qualsiasi) risposta che coinvolge constexpr , a seconda dell’interpretazione dello spec.

[AGGIORNATO 2016.01.31]

Poiché a qualcuno non è piaciuta la mia risposta precedente perché ha evitato l’intero aspetto della compile time string compare aspetto dell’OP raggiungendo l’objective senza necessità di confronti tra stringhe, ecco una risposta più dettagliata.

Non puoi! Non in C98 o C99. Nemmeno in C11. Nessuna quantità di manipolazione MACRO lo cambierà.

La definizione di const-expression utilizzata in #if non consente le stringhe.

Permette i caratteri, quindi se ti limiti ai personaggi potresti usare questo:

 #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 

Puoi! In C ++ 11. Se si definisce una funzione di helper del tempo di compilazione per il confronto.

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

Quindi, alla fine, dovrai cambiare il modo in cui raggiungi il tuo objective di scegliere i valori di stringa finale per USER e USER_VS .

Non è ansible eseguire il confronto delle stringhe di tempo di compilazione in C99, ma è ansible eseguire la scelta del tempo di compilazione delle stringhe.

Se davvero si devono fare comparazioni temporali di compilazione, allora è necessario passare a C ++ 11 o versioni più recenti che consentono tale funzionalità.

[SEGUE LA RISPOSTA ORIGINALE]

Provare:

 #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 

AGGIORNAMENTO: l’incollatura dei token ANSI è a volte meno ovvia. ;-D

Mettere un singolo # prima di una macro lo fa cambiare in una stringa del suo valore, invece del suo valore nullo.

Mettere un doppio ## tra due token fa in modo che vengano concatenati in un singolo token.

Quindi, la macro USER_VS ha l’espansione jack_VS o queen_VS , a seconda di come viene impostato USER .

La macro stringify S(...) utilizza la macrozione indiretta in modo che il valore della macro denominata venga convertito in una stringa. invece del nome della macro.

Quindi USER##_VS diventa jack_VS (o queen_VS ), a seconda di come si imposta USER .

Successivamente, quando la macro stringify viene utilizzata come S(USER_VS) il valore di USER_VS ( jack_VS in questo esempio) viene passato al punto di riferimento indiretto S_(jack_VS) che converte il suo valore ( queen ) in una stringa "queen" .

Se si imposta USER su queen il risultato finale è la stringa "jack" .

Per la concatenazione di token, consultare: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

Per la conversione delle stringhe di token, consultare: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

[AGGIORNATO 2015.02.15 per correggere un errore di battitura.]

Utilizza valori numerici anziché stringhe.

Infine, per convertire le costanti JACK o QUEEN in una stringa, utilizzare gli operatori stringize (e / o tokenize).

Il seguente ha funzionato per me con clang. Permette ciò che appare come confronto simbolico di valori macro. #error xxx è solo per vedere cosa fa veramente il compilatore. Sostituendo la definizione del gatto con #define cat (a, b) a ## b si rompono le cose.

 #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 

Come già detto sopra, il preprocessore ISO-C11 non supporta il confronto delle stringhe. Tuttavia, il problema di assegnare una macro con il “valore opposto” può essere risolto con “token pasting” e “table access”. La semplice macro-soluzione concatenata / stringify di Jesse fallisce con gcc 5.4.0 perché la stringizzazione viene eseguita prima della valutazione della concatenazione (conforms a ISO C11). Tuttavia, può essere risolto:

 #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 prima riga (macro P_() ) aggiunge un punto di riferimento per lasciare che la riga successiva (macro VS() ) termini la concatenazione prima della stringizzazione (vedi Perché ho bisogno del doppio strato di riferimento indiretto per le macro? ). Le macro di stringhe ( S() e S_() ) provengono da Jesse.

La tabella (macro jack_VS e queen_VS ) che è molto più facile da mantenere rispetto alla costruzione if-then-else dell’OP è di Jesse.

Infine, il prossimo blocco a quattro righe richiama le macro in stile funzione. L’ultimo blocco di quattro righe è della risposta di Jesse.

Memorizzando il codice in foo.c e invocando il preprocessore gcc -nostdinc -E foo.c restituisce:

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

L’output è come previsto. L’ultima riga mostra che la macro USER_VS non è stata espansa prima della stringizzazione.

Le risposte di Patrick e Jesse Chisholm mi hanno fatto fare quanto segue:

 #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 

Invece di #define USER 'Q' #define USER QUEEN dovrebbe anche funzionare ma non è stato testato funziona anche e potrebbe essere più facile da gestire.

Se le tue stringhe sono costanti di tempo di compilazione (come nel tuo caso) puoi usare il seguente trucco:

 #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 

Il compilatore può dire in anticipo il risultato dello strcmp e sostituirà lo strcmp con il suo risultato, fornendo così un #define che può essere confrontato con le direttive del preprocessore. Non so se c’è differenza tra compilatori / dipendenze sulle opzioni del compilatore, ma ha funzionato per me su GCC 4.7.2.

EDIT: dopo ulteriori indagini, sembra che questa sia un’estensione toolchain, non un’estensione GCC, quindi prendilo in considerazione …

È semplice, penso che tu possa solo dire

 #define NAME JACK #if NAME == queen