web-dev-qa-db-fra.com

Comment comparer des chaînes dans des directives préprocesseur conditionnelles C

Je dois faire quelque chose comme ça en C. Cela ne fonctionne que si j'utilise un personnage, 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
72
frx08

Je ne pense pas qu'il y ait un moyen de faire des comparaisons de chaînes de longueur variable complètement dans les directives du 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 reformuler un peu le code et utiliser le code C à la place. 

55
Brian R. Bondy

[MISE À JOUR: 2018.05.03]

MISE EN GARDE: 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é, alors que de nombreux commentateurs utilisaient un compilateur différent.

Citant la réponse de Shafik Yaghmour à l'adresse: Calcul de la longueur d'une chaîne C au moment de la compilation. Est-ce vraiment un constexpr?

Les expressions constantes ne sont pas garanties pour être évaluées à la compilation Pour le moment, nous avons seulement une citation non normative tirée du projet de norme C++ section 5.19 Expressions constantes qui dit ceci cependant:

[...]> [Note: les expressions constantes peuvent être évaluées pendant traduction. — note de fin]

Cette Parole can fait toute la différence dans le monde.

Donc, YMMV sur cette (ou toute autre) réponse impliquant constexpr, en fonction de l'interprétation de la spécification donnée par l'auteur du compilateur.

[MISE À JOUR 2016.01.31]

Comme certains n'aimaient pas ma réponse précédente parce que évitait tout l'aspect compile time string compare du PO en réalisant l'objectif sans qu'il soit nécessaire de comparer les chaînes, voici une réponse plus détaillée.

Tu ne peux pas! Pas en C98 ou C99. Pas même en C11. Aucune quantité de manipulation MACRO ne changera cela.

La définition de const-expression utilisée dans le #if n'autorise pas les chaînes.

Cela 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'assistance à 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 et USER_VS.

Vous ne pouvez pas faire de comparaison de chaîne de temps de compilation en C99, mais vous pouvez faire le temps de compilation en choisissant des chaînes.

Si vous devez réellement faire des comparaisons de temps de compilation, vous devez passer à C++ 11 ou à des variantes plus récentes autorisant cette fonctionnalité.

[RÉPONSES ORIGINALES SUIVANT]

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

UPDATE: Le collage de jetons ANSI est parfois moins qu'évident. ;-RÉ

Le fait de placer un seul # 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 entraîne leur concaténation en un seul jeton.

Ainsi, la macro USER_VS a l'extension jack_VS ou queen_VS, selon la façon dont vous définissez USER.

La macro stringifyS(...) utilise la macro indirection pour que la valeur de la macro nommée soit convertie en chaîne. au lieu du nom de la macro.

Ainsi, USER##_VS devient jack_VS (ou queen_VS), selon la manière dont vous définissez USER.

Plus tard, lorsque la macro stringify est utilisée en tant que S(USER_VS), la valeur de USER_VS (jack_VS dans cet exemple) est transmise à l'étape d'indirection S_(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

[UPDATED 2015.02.15 pour corriger une faute de frappe.]

19
Jesse Chisholm

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

7
Patrick

Ce qui suit a fonctionné pour moi avec clang. Permet ce qui apparaît comme une comparaison de valeur macro symbolique. #error xxx est juste pour voir ce que le compilateur fait vraiment. Remplacement chat définition avec #define cat (a, b) a ## b casse des 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
3

Comme déjà indiqué ci-dessus, le préprocesseur ISO-C11 ne prend pas en charge la comparaison de chaînes pas. 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énation/stringify de Jesse échoue avec gcc 5.4.0 car la stringization est effectuée avant l’évaluation de la concaténation (conforme à ISO C11). Cependant, cela 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 (macro VS()) 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() et S_()) proviennent de Jesse.

La table (macros jack_VS et queen_VS) qui est beaucoup plus facile à gérer que la construction if-then-else de l'OP provient 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 dans foo.c et appeler le préprocesseur gcc -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 indique que la macro USER_VS est pas développée avant la chaîne.

1
hermannk

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

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.

1
benni

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 de strcmp à l'avance et remplacera strcmp par son résultat, vous donnant ainsi un #define qui peut être comparé aux directives du pré-processeur. Je ne sais pas s’il existe un écart entre les compilateurs/dépendances des options du compilateur, mais cela a fonctionné pour moi dans GCC 4.7.2.

EDIT: après un examen approfondi, il semble que ceci soit une extension d’outil, pas une extension de GCC, alors prenez cela en considération ...

0
EpsilonVector
#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 et se terminant toujours par un caractère nul final.

0
yan bellavance