Je dois faire quelque chose comme ça en C. Cela ne fonctionne que si j'utilise un caractère, mais j'ai besoin d'une chaîne. Comment puis-je faire ceci?
#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
la source
la source
Réponses:
Je ne pense pas qu'il existe un moyen de faire des comparaisons de chaînes de longueur variable complètement dans les directives de préprocesseur. Vous pourriez peut-être faire ce qui suit:
#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 vous pouvez refactoriser un peu le code et utiliser le code C à la place.
la source
#define USER_VS (3 - USER)
dans ce cas précis. :)[MISE À JOUR: 2018.05.03]
CAVEAT : Tous les compilateurs n'implémentent pas la spécification C ++ 11 de la même manière. Le code ci-dessous fonctionne dans le compilateur sur lequel j'ai testé, tandis que de nombreux commentateurs utilisaient un compilateur différent.
Citant la réponse de Shafik Yaghmour à: Calcul de la longueur d'une chaîne C au moment de la compilation. Est-ce vraiment un constexpr?
Ce mot
can
fait toute la différence dans le monde.Donc, YMMV sur cette (ou n'importe quelle) réponse impliquant
constexpr
, selon l'interprétation de l'auteur du compilateur de la spécification.[MISE À JOUR 2016.01.31]
Comme certains n'aimaient pas ma réponse précédente car elle évitait tout l'
compile time string compare
aspect de l'OP en réalisant l'objectif sans avoir besoin de comparaisons de chaînes, voici une réponse plus détaillée.Vous ne pouvez pas! Pas dans C98 ou C99. Pas même en C11. Aucune manipulation MACRO ne changera cela.
La définition de
const-expression
utilisé dans#if
n'autorise pas les chaînes.Il autorise les caractères, donc si vous vous limitez aux caractères, vous pouvez utiliser ceci:
#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
Vous pouvez! En C ++ 11. Si vous définissez une fonction d'aide à la compilation pour la comparaison.
// 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
Donc, en fin de compte, vous devrez changer la façon dont vous atteignez votre objectif de choisir les valeurs de chaîne finales pour
USER
etUSER_VS
.Vous ne pouvez pas effectuer de comparaison de chaînes au moment de la compilation en C99, mais vous pouvez effectuer le choix des chaînes au moment de la compilation.
Si vous devez vraiment faire des comparaisons de temps de compilation, vous devez passer à C ++ 11 ou à des variantes plus récentes qui permettent cette fonctionnalité.
[RÉPONSE ORIGINALE SUIT]
Essayer:
#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
MISE À JOUR: le collage de jetons ANSI est parfois moins qu'évident. ;-RÉ
Mettre un single
#
avant une macro entraîne sa modification en une chaîne de sa valeur, au lieu de sa valeur nue.Mettre un double
##
entre deux jetons les fait être concaténés en un seul jeton.Ainsi, la macro
USER_VS
a l'expansionjack_VS
ouqueen_VS
, selon la façon dont vous définissezUSER
.La macro stringify
S(...)
utilise l'indirection de macro pour que la valeur de la macro nommée soit convertie en chaîne. au lieu du nom de la macro.Ainsi
USER##_VS
devientjack_VS
(ouqueen_VS
), selon la façon dont vous définissezUSER
.Plus tard, lorsque la macro stringify est utilisée comme
S(USER_VS)
valeur deUSER_VS
(jack_VS
dans cet exemple) est passée à l'étape d'indirectionS_(jack_VS)
qui convertit sa valeur (queen
) en chaîne"queen"
.Si vous définissez
USER
sur,queen
le résultat final est la chaîne"jack"
.Pour la concaténation de jetons, voir: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
Pour la conversion de chaîne de jeton, voir: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
[MISE À JOUR 2015.02.15 pour corriger une faute de frappe.]
la source
#if 0 == c_strcmp( USER, JACK )
enconstexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
Votre exemple fonctionne uniquement parce que USER est JACK. Si USER était REINE, il diraitUSER IS QUEEN
etUSER_VS IS QUEEN
constexpr
) à partir de directives de préprocesseur.Ce qui suit a fonctionné pour moi avec clang. Permet ce qui apparaît comme une comparaison de valeurs de macro symbolique. #error xxx est juste pour voir ce que fait vraiment le compilateur. Le remplacement de la définition de chat par #define cat (a, b) a ## b casse les choses.
#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
la source
Utilisez des valeurs numériques au lieu de chaînes.
Enfin, pour convertir les constantes JACK ou QUEEN en chaîne, utilisez les opérateurs stringize (et / ou tokenize).
la source
Comme déjà indiqué ci-dessus, le préprocesseur ISO-C11 ne prend pas en charge la comparaison de chaînes. Cependant, le problème de l'affectation d'une macro avec la «valeur opposée» peut être résolu avec le «collage de jetons» et «l'accès à la table». La macro-solution simple concaténer / stringify de Jesse échoue avec gcc 5.4.0 car la stringisation est effectuée avant l'évaluation de la concaténation (conformément à ISO C11). Cependant, il peut être corrigé:
#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 première ligne (macro
P_()
) ajoute une indirection pour laisser la ligne suivante (macroVS()
) terminer la concaténation avant la stringisation (voir Pourquoi ai-je besoin d'une double couche d'indirection pour les macros? ). Les macros de stringisation (S()
etS_()
) proviennent de Jesse.La table (macros
jack_VS
etqueen_VS
) qui est beaucoup plus facile à maintenir que la construction if-then-else de l'OP est de Jesse.Enfin, le bloc de quatre lignes suivant appelle les macros de style fonction. Le dernier bloc de quatre lignes provient de la réponse de Jesse.
Stocker le code
foo.c
et appeler le préprocesseurgcc -nostdinc -E foo.c
donne:# 1 "foo.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "foo.c" # 9 "foo.c" "queen" "jack" "jack" "queen" "jack" "USER_VS"
La sortie est comme prévu. La dernière ligne montre que la
USER_VS
macro n'est pas développée avant la stringisation.la source
#if (S(USER)=="jack")
- J'obtiens une erreur de préprocesseur lors de l'utilisation de"
-error: invalid token at start of a preprocessor expression
.Si vos chaînes sont des constantes de temps de compilation (comme dans votre cas), vous pouvez utiliser l'astuce suivante:
#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
Le compilateur peut dire le résultat du strcmp à l'avance et remplacera le strcmp par son résultat, vous donnant ainsi une #define qui peut être comparée aux directives du préprocesseur. Je ne sais pas s'il y a une différence entre les compilateurs / dépendance sur les options du compilateur, mais cela a fonctionné pour moi sur GCC 4.7.2.
EDIT: après une enquête plus approfondie, il semble que ce soit une extension de chaîne d'outils, pas une extension GCC, alors prenez cela en considération ...
la source
$
une sorte d'extension de pré-processeur?Les réponses de Patrick et de Jesse Chisholm m'ont fait faire ce qui suit:
#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
Au lieu de
#define USER 'Q'
#define USER QUEEN
devrait également fonctionner mais n'a pas été testéfonctionne également et pourrait être plus facile à manipuler.EDIT: D'après le commentaire de @ Jean-François Fabre j'ai adapté ma réponse.
la source
(s==QUEEN?1:0)
par(s==QUEEN)
vous n'avez pas besoin du ternaire, le résultat est déjà un booléen#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
il s'agit essentiellement d'un tableau de caractères statiques de longueur fixe initialisé manuellement au lieu d'un tableau de caractères statiques de longueur variable initialisé automatiquement se terminant toujours par un caractère nul de fin
la source
Vous ne pouvez pas faire cela si USER est défini comme une chaîne entre guillemets.
Mais vous pouvez le faire si USER est juste JACK, QUEEN ou Joker ou autre.
Il y a deux astuces à utiliser:
#define JACK
faire quelque choseAlors commençons par:
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
Maintenant, si j'écris
JACK_QUEEN_OTHER(USER)
et que USER est JACK, le préprocesseur le transforme enEXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
La deuxième étape est la concaténation:
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
maintenant ,
JACK_QUEEN_OTHER(USER)
devientEXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
Cela donne la possibilité d'ajouter un certain nombre de virgules selon qu'une chaîne correspond ou non:
#define ReSeRvEd_JACK x,x,x #define ReSeRvEd_QUEEN x,x
Si USER est JACK,
JACK_QUEEN_OTHER(USER)
devientEXPANSION2(x,x,x, 1, 2, 3)
Si USER est REINE,
JACK_QUEEN_OTHER(USER)
devientEXPANSION2(x,x, 1, 2, 3)
Si USER est autre,
JACK_QUEEN_OTHER(USER)
devientEXPANSION2(ReSeRvEd_other, 1, 2, 3)
À ce stade, quelque chose de critique s'est produit: le quatrième argument de la macro EXPANSION2 est 1, 2 ou 3, selon que l'argument d'origine passé était jack, queen ou autre. Tout ce que nous avons à faire est donc de le sélectionner. Pour de longues raisons, nous aurons besoin de deux macros pour la dernière étape; ce seront EXPANSION2 et EXPANSION3, même si l'une semble inutile.
En mettant tout cela ensemble, nous avons ces 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
Et vous pourriez les utiliser comme ceci:
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 }
Lien godbolt obligatoire: https://godbolt.org/z/8WGa19
la source
C'est simple, je pense que tu peux juste dire
#define NAME JACK #if NAME == queen
la source