Possiamo avere macro ricorsive?

Voglio sapere se possiamo avere macro ricorsive in C / C ++? In caso affermativo, fornire un esempio di esempio.

Seconda cosa: perché non riesco a eseguire il codice qui sotto? Qual è l’errore che sto facendo? È a causa delle macro ricorsive?

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

Il tuo compilatore probabilmente fornisce un’opzione solo per pre-processare, non in realtà compilare. Questo è utile se stai cercando di trovare un problema in una macro. Ad esempio usando 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)) < 

Come puoi vedere, non è ricorsivo. pr(x) viene sostituito solo una volta durante la pre-elaborazione. La cosa importante da ricordare è che tutto il pre-processore sostituisce ciecamente una stringa di testo con un'altra, in realtà non valuta espressioni come (x == 1) .

Il motivo per cui il tuo codice non verrà compilato è che pr(5 -1) non è stato sostituito dal pre-processore, quindi finisce nella sorgente come una chiamata a una funzione non definita.

Le macro non si espandono direttamente in modo ricorsivo, ma esistono soluzioni alternative. Quando il preprocessore esegue la scansione e l’espansione di pr(5) :

 pr(5) ^ 

crea un contesto disabilitante, in modo tale che quando rivede pr nuovo:

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

diventa blu dipinto e non può più espandersi, qualunque cosa proviamo. Ma possiamo evitare che la nostra macro venga dipinta di blu usando espressioni differite e qualche indizio indiretto:

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

Quindi ora si espanderà in questo modo:

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

Il che è perfetto, perché pr non è mai stato dipinto di blu. Abbiamo solo bisogno di applicare un’altra scansione per farlo espandere ulteriormente:

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

Possiamo applicare due scansioni per farlo espandere ulteriormente:

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

Tuttavia, poiché non esiste una condizione di terminazione, non possiamo mai applicare scansioni sufficienti. Non sono sicuro di cosa vuoi ottenere, ma se sei curioso su come creare macro ricorsive, ecco un esempio di come creare una macro di ripetizione ricorsiva.

Prima una macro per applicare molte scansioni:

 #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__ 

Successivamente, una macro concat è utile per la corrispondenza dei pattern:

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

Contatori di incremento e decremento:

 #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 

Alcuni macro utili per i condizionali:

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

Mettendo tutto insieme possiamo creare una macro di ripetizione:

 #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 

Quindi, sì con alcuni accorgimenti, puoi avere macro ricorsive in C / C ++.

Non dovresti avere macro ricorsive in C o C ++.

La lingua pertinente dallo standard C ++, paragrafo 16.3.4, paragrafo 2:

Se il nome della macro da sostituire viene trovato durante questa scansione dell’elenco di sostituzione (senza includere il resto dei token di preelaborazione del file sorgente), non viene sostituito. Inoltre, se eventuali sostituzioni annidate incontrano il nome della macro che viene sostituita, non viene sostituita. Questi token di preprocessing del nome macro non posizionati non sono più disponibili per ulteriori sostituzioni, anche se sono (ri) esaminati in seguito in contesti in cui il token di pre-elaborazione del nome della macro sarebbe stato altrimenti sostituito.

C’è un po ‘di flessibilità in questa lingua. Con più macro che si richiamano l’una con l’altra, c’è un’area grigia dove quella dicitura non dice esattamente cosa dovrebbe essere fatto. C’è un problema attivo rispetto allo standard C ++ per quanto riguarda questo problema di avvocato linguistico; vedi http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268 .

Ignorando quel problema di avvocato linguistico, ogni venditore di compilatori comprende l’intento:

Le macro ricorsive non sono consentite in C o in C ++.

Molto probabilmente non sei in grado di eseguirlo perché non puoi compilarlo. Inoltre, se fosse compilato correttamente, restituirebbe sempre 1. Volevi dire (n==1)? 1 : n * pr(n-1) (n==1)? 1 : n * pr(n-1) .

I macro non possono essere ricorsivi. Secondo il capitolo 16.3.4.2 (grazie a Loki Astari), se la macro corrente viene trovata nell’elenco di sostituzione, viene lasciata così com’è, quindi il tuo pr nella definizione non verrà modificato:

Se il nome della macro da sostituire viene trovato durante questa scansione dell’elenco di sostituzione (esclusi i restanti token di elaborazione preliminari del file sorgente), non viene sostituito. Inoltre, se eventuali sostituzioni annidate incontrano il nome della macro che viene sostituita, non viene sostituita. Questi token di preprocessing del nome macro non posizionati non sono più disponibili per ulteriori sostituzioni, anche se sono (ri) esaminati in seguito in contesti in cui il token di pre-elaborazione del nome della macro sarebbe stato altrimenti sostituito.

La tua chiamata:

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

è stato convertito dal preprocessore in:

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

Durante questo, la definizione della macro pr è 'persa' e il compilatore mostra un errore come "'pr' non è stato dichiarato in questo ambito (fatto)" perché non esiste una funzione denominata pr .

L'uso di macro non è consigliato in C ++. Perché non scrivi semplicemente una funzione?

In questo caso potresti persino scrivere una funzione modello in modo che venga risolta in fase di compilazione e si comporti come un valore costante:

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

Non è ansible avere macro ricorsive in C o C ++.