Como comparar seqüências de caracteres em diretivas de pré-processador condicional de C

Eu tenho que fazer algo assim em C. Ele só funciona se eu usar um char, mas eu preciso de uma string. Como posso fazer isso?

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

Eu não acho que há uma maneira de fazer comparações de seqüência de comprimento variável completamente em diretivas de pré-processador. Você poderia, talvez, fazer o seguinte:

 #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 

Ou você poderia refatorar o código um pouco e usar o código C.

[ATUALIZAÇÃO: 2018.05.03]

CAVEAT : Nem todos os compiladores implementam a especificação C ++ 11 da mesma maneira. O código abaixo funciona no compilador que testei, enquanto muitos comentadores usaram um compilador diferente.

Citando a resposta de Shafik Yaghmour em: Comprimento de computação de uma string C em tempo de compilation. Isso é realmente um constexpr?

Não é garantido que as expressões constantes sejam avaliadas em tempo de compilation, só temos uma citação não-normativa do rascunho da seção padrão C ++ 5.19 Expressões constantes que dizem isso:

[…]> [Nota: expressões constantes podem ser avaliadas durante a tradução. – nota final]

Essa palavra can fazer toda a diferença no mundo.

Então, YMMV nesta (ou qualquer) resposta envolvendo constexpr , dependendo da interpretação da especificação pelo compilador.

[ATUALIZADO 2016.01.31]

Como alguns não gostaram da minha resposta anterior porque ela evitou todo o compile time string compare aspecto do OP ao realizar o objective sem necessidade de comparação de strings, aqui está uma resposta mais detalhada.

Você não pode! Não em C98 ou C99. Nem mesmo no C11. Nenhuma quantidade de manipulação MACRO irá mudar isso.

A definição de const-expression usada no #if não permite strings.

Ele permite caracteres, por isso, se você se limitar aos caracteres, poderá usar isto:

 #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 

Você pode! Em C ++ 11. Se você definir uma function auxiliar do tempo de compilation para a comparação.

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

Então, em última análise, você terá que mudar a maneira como você ajusta o seu objective de escolher valores finais de string para USER e USER_VS .

Você não pode fazer compilation de tempo de compilation no C99, mas você pode fazer compilation de tempo de compilation de seqüências de caracteres.

Se você realmente deve compilar comparações de sting de tempo de compilation, então você precisa mudar para C ++ 11 ou variantes mais novas que permitem esse recurso.

[RESPOSTA ORIGINAL SIGA]

Experimentar:

 #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 

ATUALIZAÇÃO: o token de token ANSI às vezes é menos do que óbvio. ;-D

Colocar um único # antes de uma macro faz com que ela seja alterada em uma sequência de seu valor, em vez de seu valor nulo.

Colocar um ## duplo entre dois tokens faz com que eles sejam concatenados em um único token.

Portanto, a macro USER_VS possui a expansão jack_VS ou queen_VS , dependendo de como você configurou USER .

A macro de stringify S(...) usa a indirecção da macro para que o valor da macro nomeada seja convertido em uma string. em vez do nome da macro.

Portanto, USER##_VS se torna jack_VS (ou queen_VS ), dependendo de como você configurou USER .

Posteriormente, quando a macro stringify é usada como S(USER_VS) o valor de USER_VS ( jack_VS neste exemplo) é passado para a etapa indireta S_(jack_VS) que converte seu valor ( queen ) em uma string "queen" .

Se você definir USER como queen , o resultado final será a string "jack" .

Para concatenação de token, consulte: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

Para conversão de string de token, consulte: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

[ATUALIZADO 2015.02.15 para corrigir um erro de digitação.]

Use valores numéricos em vez de seqüências de caracteres.

Finalmente, para converter as constantes JACK ou QUEEN em uma string, use os operadores de stringize (e / ou tokenize).

O seguinte funcionou para mim com clang. Permite o que aparece como comparação de valor macro simbólico. #error xxx é apenas para ver o que o compilador realmente faz. Substituindo a definição de gato por #define cat (a, b) a ## b quebra as coisas.

 #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 já foi dito acima, o pré-processador ISO-C11 não suporta comparação de strings. No entanto, o problema de atribuir uma macro com o “valor oposto” pode ser resolvido com “token pasting” e “table access”. A macro-solução de concatenar / stringify simples do Jesse falha com o gcc 5.4.0 porque a stringização é feita antes da avaliação da concatenação (em conformidade com a ISO C11). No entanto, isso pode ser corrigido:

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

A primeira linha (macro P_() ) adiciona uma indireta para permitir que a próxima linha (macro VS() ) termine a concatenação antes da string (veja Por que eu preciso de dupla camada de indireto para macros? ). As macros de stringização ( S() e S_() ) são de Jesse.

A tabela (macros jack_VS e queen_VS ) que é muito mais fácil de manter do que a construção if-then-else do OP é de Jesse.

Finalmente, o próximo bloco de quatro linhas invoca as macros de estilo de function. O último bloco de quatro linhas é da resposta de Jesse.

Armazenando o código em foo.c e invocando o pré-processador gcc -nostdinc -E foo.c yields:

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

A saída é conforme o esperado. A última linha mostra que a macro USER_VS não é expandida antes da stringização.

As respostas de Patrick e de Jesse Chisholm fizeram-me o seguinte:

 #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 

Em vez de #define USER 'Q' #define USER QUEEN também deve funcionar, mas não foi testado também funciona e pode ser mais fácil de manusear.

Se suas strings forem constantes de tempo de compilation (como no seu caso), você pode usar o seguinte truque:

 #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 

O compilador pode informar o resultado do strcmp antecipadamente e irá replace o strcmp pelo seu resultado, dando-lhe um #define que pode ser comparado com as diretivas do pré-processador. Eu não sei se há alguma variação entre os compiladores / dependência nas opções do compilador, mas funcionou para mim no GCC 4.7.2.

EDIT: após uma investigação mais aprofundada, parece que esta é uma extensão toolchain, não extensão GCC, então leve isso em consideração …

É simples, eu acho que você pode apenas dizer

 #define NAME JACK #if NAME == queen