I macro hanno un significato molto diverso in C (o C ++) che ad es in Common Lisp o Scheme (o qualche linguaggio di programmazione omoiconico ).
In particolare, in Lisp o Scheme puoi avere macro con qualche ambito di associazione locale. . In C o C ++ l'ambito è testuale, sempre dal #define
fino alla fine dell'unità di traduzione o qualche #undef
. E i macro C / C ++ non possono definire altre macro (ma potresti farlo in Lisp)
In C e C ++, il preprocessore è la prima fase del compilatore e opera a livello testuale (in In particolare, il preprocessore C / C ++ non vede alcun albero di sintassi astratto , solo lessicale token). Il compilatore vero e proprio vede solo la forma preelaborata (quindi il tuo punto 3 è vero, ei tuoi punti 1 e 2 diventano irrilevanti). Quindi le macro vivono in un mondo diverso rispetto ai valori o alle variabili. In particolare, le macro potrebbero essere definite senza nemmeno essere un'espressione o un'istruzione ben formata, come in
int counter;
/// UGLY!
#define MY_BEGIN { counter++;
#define MY_END(N) N--; }
void myfun(void)
MY_BEGIN printf("counter=%d\n", counter); MY_END(counter)
Ma sento che il codice sopra è brutto!
Un compilatore C o C ++ ti dà la possibilità di ottenere il modulo preelaborato. Con GCC utilizza ad es. gcc -C -E foo.c > foo.i
per entrare nel file foo.i
(che puoi controllare con qualche editor o cercapersone) la forma preelaborata del file sorgente foo.c
... e leggi la documentazione di cpp , cioè del preprocessore GCC.
Si noti che i macro C / C ++ non sono in grado di generare facilmente alcun tipo di codice. Ad esempio, non puoi codificare generatori di parser come bisonte o ANTLR con solo macro C / C ++ (avete bisogno di alcuni esterni Strumento per la generazione di codici C come bison
). Il C ++ ha modelli , ma ad esempio non è possibile codificare facilmente un generatore di stampanti (cioè stampare tutto campi di alcuni struct
dati come argomento modello) o un generatore serializzatore con essi. Tali cose sono possibili in Lisp.
In Common Lisp , i macro sono omogenei con il linguaggio di programmazione e sono valori . il processo valutazione e compilazione è molto diverso da C. Ad esempio, con SBCL implementazione di Common Lisp, il valutatore e il compilatore fanno parte dell'applicazione ogni e le applicazioni in Lisp generano di routine alcune espressioni Lisp in fase di runtime quindi compilarli (anche in fase di esecuzione). Quindi il codice stesso sta cambiando in fase di runtime!
Molto grossolanamente , nelle macro Lisp c'è un qualche tipo di valore (vale a dire un tipo di dati); in Schema o MELT (una lingua specifica per il dominio estendere e personalizzare GCC ), le definizioni macro introducono un qualche tipo di legame. (Ma le cose sono in effetti più sottili, quindi questa è una semplificazione ed è in qualche modo "sbagliato"). Leggi le quasi-quotazioni .
Il libro di C.Queinnec Lisp In Small Pieces lo spiega abbastanza bene. Anche il libro di Scott Programming Pragmatic Language è una lettura interessante.