Tenho que fazer algo assim em C. Funciona apenas se eu usar um char, mas 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
c
conditional
c-preprocessor
frx08
fonte
fonte
Respostas:
Não acho que haja uma maneira de fazer comparações de strings de comprimento variável completamente em diretivas de pré-processador. Você talvez pudesse 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ê pode refatorar um pouco o código e usar o código C.
fonte
#define USER_VS (3 - USER)
, neste caso específico. :)[ATUALIZAÇÃO: 03.05.2018]
CAVEAT : Nem todos os compiladores implementam a especificação C ++ 11 da mesma maneira. O código a seguir funciona no compilador que testei, enquanto muitos comentadores usaram um compilador diferente.
Citando a resposta de Shafik Yaghmour em: Calculando o comprimento de uma string C em tempo de compilação. Este é realmente um constexpr?
Aquela palavra
can
faz toda a diferença do mundo.Então, YMMV nesta (ou qualquer) resposta envolvendo
constexpr
, dependendo da interpretação do escritor do compilador das especificações.[ATUALIZADO em 31.01.2016]
Como alguns não gostaram da minha resposta anterior, porque evitou o todo
compile time string compare
aspecto do OP ao cumprir a meta sem a necessidade de comparações 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 mudará isso.
A definição de
const-expression
usado no#if
não permite strings.Ele permite caracteres, portanto, se você se limitar a eles, poderá usar o seguinte:
#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 função auxiliar de tempo de compilação 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ê realiza seu objetivo de escolher os valores finais da string para
USER
eUSER_VS
.Você não pode fazer comparações de strings em tempo de compilação no C99, mas pode fazer a escolha de strings em tempo de compilação.
Se você realmente deve fazer comparações de tempo de compilação, então você precisa mudar para C ++ 11 ou variantes mais recentes que permitam esse recurso.
[SEGUE RESPOSTA ORIGINAL]
Tentar:
#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: a colagem do token ANSI às vezes não é tão óbvia. ;-D
Colocando um único
#
antes de uma macro faz com que seja alterado para uma string de seu valor, em vez de seu valor vazio.Colocando um duplo
##
entre dois tokens faz com que eles sejam concatenados em um único token.Então, a macro
USER_VS
tem a expansãojack_VS
ouqueen_VS
, dependendo de como você definirUSER
.A macro stringify
S(...)
usa indireção de macro para que o valor da macro nomeada seja convertido em uma string. em vez do nome da macro.Assim,
USER##_VS
torna-sejack_VS
(ouqueen_VS
), dependendo de como você defineUSER
.Posteriormente, quando a macro stringify é usada,
S(USER_VS)
o valor deUSER_VS
(jack_VS
neste exemplo) é passado para a etapa de indireçãoS_(jack_VS)
que converte seu valor (queen
) em uma string"queen"
.Se você definir
USER
comoqueen
, 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 EM 15/02/2015 para corrigir um erro de digitação.]
fonte
#if 0 == c_strcmp( USER, JACK )
paraconstexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
Seu exemplo funciona apenas porque USER é JACK. Se USER fosse QUEEN, diriaUSER IS QUEEN
eUSER_VS IS QUEEN
constexpr
) de diretivas de pré-processador.O seguinte funcionou para mim com clang. Permite o que aparece como comparação simbólica de valores macro. #error xxx é apenas para ver o que o compilador realmente faz. Substituir 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
fonte
Use valores numéricos em vez de strings.
Finalmente, para converter as constantes JACK ou QUEEN em uma string, use os operadores stringize (e / ou tokenize).
fonte
Como já foi dito acima, o pré-processador ISO-C11 não suporta comparação de strings. No entanto, o problema de atribuir a uma macro o “valor oposto” pode ser resolvido com “colagem de token” e “acesso à tabela”. A macro-solução concatenate / stringify simples de Jesse falha com gcc 5.4.0 porque a stringização é feita antes da avaliação da concatenação (em conformidade com ISO C11). No entanto, 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 indireção para permitir que a próxima linha (macroVS()
) termine a concatenação antes da stringização (consulte Por que preciso de uma camada dupla de indireção para macros? ). As macros de stringização (S()
eS_()
) são de Jesse.A tabela (macros
jack_VS
equeen_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 função. O último bloco de quatro linhas é da resposta de Jesse.
Armazenar o código
foo.c
e invocar o pré-processadorgcc -nostdinc -E foo.c
produz:# 1 "foo.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "foo.c" # 9 "foo.c" "queen" "jack" "jack" "queen" "jack" "USER_VS"
A saída é a esperada. A última linha mostra que a
USER_VS
macro não é expandida antes da stringização.fonte
#if (S(USER)=="jack")
- Recebo um erro de pré-processador ao usar o"
-error: invalid token at start of a preprocessor expression
.Se suas strings são constantes de tempo de compilação (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 com antecedência e irá substituir o strcmp com seu resultado, dando a você um #define que pode ser comparado com as diretivas do pré-processador. Não sei se há alguma variação entre compiladores / dependência de opções do compilador, mas funcionou para mim no GCC 4.7.2.
EDITAR: após uma investigação mais aprofundada, parece que esta é uma extensão do conjunto de ferramentas, não uma extensão do GCC, então leve isso em consideração ...
fonte
$
algum tipo de extensão de pré-processador?A resposta de Patrick e Jesse Chisholm me fez fazer o seguinte:
#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
Ao invés de
#define USER 'Q'
#define USER QUEEN
também deve funcionar, mas não foi testadotambém funciona e pode ser mais fácil de manusear.EDIT: De acordo com o comentário de @Jean-François Fabre adaptei minha resposta.
fonte
(s==QUEEN?1:0)
por(s==QUEEN)
você não precisa do ternário, o resultado já é um 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
é basicamente um array de char estático de comprimento fixo inicializado manualmente em vez de um array de char estático de comprimento variável inicializado automaticamente sempre terminando com um char nulo de terminação
fonte
Você não pode fazer isso se USER for definido como uma string entre aspas.
Mas você pode fazer isso se USER for apenas JACK ou QUEEN ou Joker ou o que for.
Existem dois truques para usar:
#define JACK
fazer nadaEntão, vamos começar com:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
Agora, se eu escrever
JACK_QUEEN_OTHER(USER)
e USER for JACK, o pré-processador transformará isso emEXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
A segunda etapa é a concatenação:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
Agora
JACK_QUEEN_OTHER(USER)
se tornaEXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
Isso dá a oportunidade de adicionar várias vírgulas de acordo com a correspondência de uma string:
#define ReSeRvEd_JACK x,x,x #define ReSeRvEd_QUEEN x,x
Se USER é JACK,
JACK_QUEEN_OTHER(USER)
torna-seEXPANSION2(x,x,x, 1, 2, 3)
Se USER é QUEEN,
JACK_QUEEN_OTHER(USER)
torna-seEXPANSION2(x,x, 1, 2, 3)
Se USER for outro,
JACK_QUEEN_OTHER(USER)
torna-seEXPANSION2(ReSeRvEd_other, 1, 2, 3)
Nesse ponto, algo crítico aconteceu: o quarto argumento para a macro EXPANSION2 é 1, 2 ou 3, dependendo se o argumento original aprovado foi valete, rainha ou qualquer outra coisa. Portanto, tudo o que temos a fazer é escolher. Por razões prolixas, precisaremos de duas macros para a última etapa; eles serão EXPANSION2 e EXPANSION3, embora um pareça desnecessário.
Juntando tudo, temos 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
E você pode usá-los assim:
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 }
Link Godbolt obrigatório: https://godbolt.org/z/8WGa19
fonte
É simples, acho que você pode apenas dizer
#define NAME JACK #if NAME == queen
fonte