Peut-on avoir des macros récursives?

Je veux savoir si nous pouvons avoir des macros récursives en C / C ++? Si oui, veuillez fournir un exemple d’exemple.

Deuxième chose: pourquoi ne puis-je pas exécuter le code ci-dessous? Quelle est l’erreur que je fais? Est-ce à cause de macros récursives?

# define pr(n) ((n==1)? 1 : pr(n-1)) void main () { int a=5; cout<<"result: "<< pr(5) <<endl; getch(); } 

Votre compilateur fournit probablement une option pour le prétraitement uniquement, pas pour la compilation. Ceci est utile si vous essayez de trouver un problème dans une macro. Par exemple, en utilisant g++ -E :

 > g++ -E recursiveMacro.c # 1 "recursiveMacro.c" # 1 "" # 1 "" # 1 "recursiveMacro.c" void main () { int a=5; cout<<"result: "<< ((5==1)? 1 : pr(5 -1)) < 

Comme vous pouvez le voir, ce n'est pas récursif. pr(x) n'est remplacé qu'une seule fois lors du prétraitement. La chose importante à retenir est que tout ce que le pré-processeur fait est de remplacer aveuglément une chaîne de texte par une autre, il n'évalue pas réellement les expressions comme (x == 1) .

La raison pour laquelle votre code ne sera pas compilé est que pr(5 -1) n'a pas été remplacé par le pré-processeur, il se retrouve donc dans la source en tant qu'appel à une fonction non définie.

Les macros ne se développent pas directement de manière récursive, mais il existe des solutions de contournement. Lorsque le préprocesseur parsing et développe pr(5) :

 pr(5) ^ 

il crée un contexte de désactivation, de sorte que lorsqu’il voit à nouveau:

 ((5==1)? 1 : pr(5-1)) ^ 

il devient peint en bleu et ne peut plus se dilater, peu importe ce que nous essayons. Mais nous pouvons empêcher que notre macro ne soit peinte en bleu en utilisant des expressions différées et certaines indirection:

 # define EMPTY(...) # define DEFER(...) __VA_ARGS__ EMPTY() # define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)() # define EXPAND(...) __VA_ARGS__ # define pr_id() pr # define pr(n) ((n==1)? 1 : DEFER(pr_id)()(n-1)) 

Alors maintenant, il s’agrandira comme ceci:

 pr(5) // Expands to ((5==1)? 1 : pr_id ()(5 -1)) 

Ce qui est parfait, car pr n’a jamais été peint en bleu. Nous avons juste besoin d’appliquer un autre scan pour le développer davantage:

 EXPAND(pr(5)) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : pr_id ()(5 -1 -1))) 

Nous pouvons appliquer deux parsings pour le développer davantage:

 EXPAND(EXPAND(pr(5))) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : ((5 -1 -1==1)? 1 : pr_id ()(5 -1 -1 -1)))) 

Cependant, comme il n’y a pas de condition de terminaison, nous ne pouvons jamais appliquer suffisamment d’parsings. Je ne suis pas sûr de ce que vous voulez accomplir, mais si vous êtes curieux de savoir comment créer des macros récursives, voici un exemple de création d’une macro de répétition récursive.

D’abord une macro pour appliquer beaucoup d’parsings:

 #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) #define EVAL5(...) __VA_ARGS__ 

Ensuite, une macro de concat qui est utile pour la correspondance de motif:

 #define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) #define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__ 

Incrémenter et décrémenter les compteurs:

 #define INC(x) PRIMITIVE_CAT(INC_, x) #define INC_0 1 #define INC_1 2 #define INC_2 3 #define INC_3 4 #define INC_4 5 #define INC_5 6 #define INC_6 7 #define INC_7 8 #define INC_8 9 #define INC_9 9 #define DEC(x) PRIMITIVE_CAT(DEC_, x) #define DEC_0 0 #define DEC_1 0 #define DEC_2 1 #define DEC_3 2 #define DEC_4 3 #define DEC_5 4 #define DEC_6 5 #define DEC_7 6 #define DEC_8 7 #define DEC_9 8 

Quelques macros utiles pour les conditions:

 #define CHECK_N(x, n, ...) n #define CHECK(...) CHECK_N(__VA_ARGS__, 0,) #define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x)) #define NOT_0 ~, 1, #define COMPL(b) PRIMITIVE_CAT(COMPL_, b) #define COMPL_0 1 #define COMPL_1 0 #define BOOL(x) COMPL(NOT(x)) #define IIF(c) PRIMITIVE_CAT(IIF_, c) #define IIF_0(t, ...) __VA_ARGS__ #define IIF_1(t, ...) t #define IF(c) IIF(BOOL(c)) #define EAT(...) #define EXPAND(...) __VA_ARGS__ #define WHEN(c) IF(c)(EXPAND, EAT) 

En mettant tout cela ensemble, nous pouvons créer une macro de répétition:

 #define REPEAT(count, macro, ...) \ WHEN(count) \ ( \ OBSTRUCT(REPEAT_INDIRECT) () \ ( \ DEC(count), macro, __VA_ARGS__ \ ) \ OBSTRUCT(macro) \ ( \ DEC(count), __VA_ARGS__ \ ) \ ) #define REPEAT_INDIRECT() REPEAT //An example of using this macro #define M(i, _) i EVAL(REPEAT(8, M, ~)) // 0 1 2 3 4 5 6 7 

Donc, avec quelques solutions de contournement, vous pouvez avoir des macros récursives en C / C ++.

Vous n’êtes pas censé avoir des macros récursives en C ou C ++.

Le langage pertinent de la norme C ++, section 16.3.4, paragraphe 2:

Si le nom de la macro remplacée est trouvé lors de l’parsing de la liste de remplacement (n’incluant pas les jetons de prétraitement du fichier source), il n’est pas remplacé. De plus, si des remplacements nesteds rencontrent le nom de la macro à remplacer, il n’est pas remplacé. Ces jetons de prétraitement de nom de macro non remplacés ne sont plus disponibles pour un remplacement ultérieur même s’ils sont (ultérieurement) examinés dans des contextes dans lesquels ce jeton de prétraitement de nom de macro aurait autrement été remplacé.

Il y a une marge de manœuvre dans cette langue. Avec plusieurs macros qui s’appellent, il ya une zone grise dans laquelle cette formulation ne dit pas tout à fait ce qui doit être fait. Il existe un problème actif par rapport à la norme C ++ en ce qui concerne ce problème d’avocat de langue. voir http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268 .

En ignorant la question de l’avocat de la langue, chaque fournisseur de compilateur comprend l’intention:

Les macros récursives ne sont pas autorisées dans C ou dans C ++.

Vous n’êtes probablement pas en mesure de l’exécuter car vous ne pouvez pas le comstackr. De plus, si elle se compilait correctement, elle retournerait toujours 1. Voulez-vous dire (n==1)? 1 : n * pr(n-1) (n==1)? 1 : n * pr(n-1) .

Les macros ne peuvent pas être récursives. Selon le chapitre 16.3.4.2 (merci Loki Astari), si la macro actuelle se trouve dans la liste de remplacement, elle rest telle quelle, donc votre pr dans la définition ne sera pas modifié:

Si le nom de la macro à remplacer est trouvé lors de l’parsing de la liste de remplacement (n’incluant pas les jetons de prétraitement du fichier source), il n’est pas remplacé. De plus, si des remplacements nesteds rencontrent le nom de la macro à remplacer, il n’est pas remplacé. Ces jetons de prétraitement de nom de macro non remplacés ne sont plus disponibles pour un remplacement ultérieur même s’ils sont (ultérieurement) examinés dans des contextes dans lesquels ce jeton de prétraitement de nom de macro aurait autrement été remplacé.

Ton appel:

 cout<<"result: "<< pr(5) < 

a été converti par préprocesseur en:

 cout<<"result: "<< (5==1)? 1 : pr(5-1) < 

Pendant ce temps, la définition de macro pr est "perdue" et le compilateur montre une erreur comme "pr" n'a pas été déclaré dans cette étendue (fait) "car il n'y a pas de fonction nommée pr .

L'utilisation de macros n'est pas encouragée en C ++. Pourquoi ne pas simplement écrire une fonction?

Dans ce cas, vous pouvez même écrire une fonction de modèle afin qu'elle soit résolue au moment de la compilation et se comportera comme une valeur constante:

 template  int pr() { pr(); } template <> int pr<1>() { return 1; } 

Vous ne pouvez pas avoir de macros récursives en C ou C ++.