Maison >développement back-end >C++ >Comment pouvez-vous obtenir une fonctionnalité de macro récursive en C, malgré les limites de l'expansion des macros ?
En programmation C, il n'est pas simple d'appliquer des macros de manière récursive aux arguments de macro. Cependant, en utilisant une solution de contournement complexe, il est possible d'obtenir une macro MAP avec la fonctionnalité souhaitée.
Pour lancer la récursivité, nous définissons une macro MAP_OUT qui sert d'espace réservé pour extension de macro :
#define MAP_OUT
Ensuite, nous créons deux macros, A et B, pour démontrer récursion :
#define A(x) x B MAP_OUT (x) #define B(x) x A MAP_OUT (x)
L'évaluation de A(blah) donne le résultat suivant :
blah B (blah)
Le préprocesseur traite B(blah) comme du texte brut puisqu'il ne s'agit pas encore d'un appel de macro. Lorsque ce texte est retraité, il se développe pour produire :
blah blah A (blah)
En réinjectant continuellement la sortie dans le préprocesseur, nous pouvons maintenir la récursion indéfiniment.
Pour automatiser cette évaluation répétée, nous utilisons la macro EVAL :
#define EVAL0(...) __VA_ARGS__ #define EVAL1(...) EVAL0 (EVAL0 (EVAL0 (__VA_ARGS__))) #define EVAL2(...) EVAL1 (EVAL1 (EVAL1 (__VA_ARGS__))) #define EVAL3(...) EVAL2 (EVAL2 (EVAL2 (__VA_ARGS__))) #define EVAL4(...) EVAL3 (EVAL3 (EVAL3 (__VA_ARGS__))) #define EVAL(...) EVAL4 (EVAL4 (EVAL4 (__VA_ARGS__)))
La macro EVAL propage ses arguments à travers une arborescence d'appels de macro, multipliant leur nombre d'évaluations par 365.
Pour éviter une récursion sans fin, nous avons besoin d'un mécanisme de terminaison. Nous définissons une macro spéciale nommée MAP_END :
#define MAP_END(...)
Lorsqu'elle est évaluée, cette macro ne fait rien, arrêtant effectivement la récursivité.
Pour choisir entre les macros récursives et de fin, nous introduisons MAP_NEXT :
#define MAP_NEXT0(item, next, ...) next MAP_OUT #define MAP_NEXT1(item, next) MAP_NEXT0 (item, next, 0) #define MAP_NEXT(item, next) MAP_NEXT1 (MAP_GET_END item, next)
MAP_NEXT compare l'élément actuel avec le marqueur de fin de liste (). Il renvoie MAP_END s'ils correspondent, ou le paramètre suivant dans le cas contraire.
En combinant ces éléments, nous pouvons créer des versions pratiques des macros A et B :
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__) #define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__)
MAP0 et MAP1 appliquent l'opération f à l'élément actuel x. Ils vérifient ensuite l'élément suivant, peek, pour déterminer s'il faut continuer ou mettre fin à la récursion.
Enfin, nous lions le tout avec la macro MAP de niveau supérieur :
#define MAP(f, ...) EVAL (MAP1 (f, __VA_ARGS__, (), 0))
Lieux MAP un marqueur de fin de liste sur la liste d'arguments et le transmet via EVAL.
En utilisant ces techniques, vous pouvez obtenir une fonctionnalité de macro récursive en C, permettant des traitement basé sur des macros.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!