2013-04-07 11 views
6

ho questo codice che funziona:C preprocessore utilizzando la staffa di un genitore macro chiusura

#include <stdio.h> 
#define A(x) x B 
#define B(x) C(x, 
#define C(x,y) y x) 
int main(void) { 
    printf(A("1") ("2") "3"); 
} 

esso stampa 132 (il punto del A macro è quello di scambiare la cosa che segue i parametri tra parentesi con tutto dopo che fino a un'altra parentesi di chiusura)

Ma se io uso che all'interno di un'altra macro:

#define Z(x) x 
printf(Z(A("1") ("2") "3")); 

ottengo l'errore di compilazione "Chiamata macro simile a una funzione non terminata".

Mi rendo conto che ciò accade perché il compilatore sta tentando di elaborare gli argomenti di Z in modo indipendente, ma ho bisogno di usare il suo parentesi di chiusura come marcatore. C'è un modo per farlo funzionare all'interno dei macro? La modifica della sintassi chiamante non è realmente un'opzione.


p.s. Prima di ottenere qualsiasi risposta parlando di ciò che è terribile da fare, state tranquilli: questo non è per codice reale. Si tratta di un problema che si è verificato durante la creazione di un programma giocattolo che utilizza define per simulare una nuova lingua all'interno di C.

risposta

3

Il modo più semplice per vedere cosa sta succedendo è cambiare un po 'il caso di test.

#define A(x) x B 
#define B(x) C(x, 
#define C(x,y) y x] /* note close square bracket instead of close paren */ 

Y(A(1)(2)3) 

preelaborati a Y(1 3 2]. Ciò è perché una fase intermedia di espansione sembrava

Y(1 C(2,3) 

a quel punto C mangiato la parentesi vicino che sembravano appartenere Y nel testo originale e sostituito con una staffa stretta.

Ora, cosa succede in modo diverso se A(1)(2)3 si trova all'interno di un argomento macro?

#define Z(x) x 
Z(A(1)(2)3) 

causa della argument prescan, lo stadio intermedio analogo di espansione è non

Z(1 C(2,3) 

ma piuttosto

1 C(2,3 

con Z squirrelled via su una nascosta "sospeso espansioni" stack. Il preprocessore è, in effetti, che applica l'aspetto testuale che l'ultimo paren chiuso appartiene a Z e C non è autorizzato a prenderlo in prestito.

Il modo meno invasivo mi viene in mente per raggiungere il tuo obiettivo originale è

#define _A(x) x B 
#define B(x) C(x, 
#define C(x,y) y x) 

#define Z(x) ZZ((_##x)) 
#define ZZ(x) ZZZ x 
#define ZZZ(x) [x] 

Z(A(1)(2)3) 

preprocessa a [1 3 2]. Usiamo l'operatore token paste per evitare che l'argomento di Z venga sottoposto a prescansione, quindi possiamo aggiungere un set aggiuntivo temporaneo di parentesi per l'uso da C. ZZ e ZZZ quindi rimuoverli nuovamente. Il problema è che si tratta di un errore se non si incolla con,, quindi è necessario aggiungere un trattino basso alla definizione di A e sarà un errore se il primo token dell'argomento Z è mai non qualcosa che può essere incollato con un token dopo un trattino basso.

Si potrebbe prendere in considerazione l'utilizzo di M4 invece di provare a calzare questo nel preprocessore C.

+0

Grazie per la spiegazione approfondita. Sfortunatamente la soluzione non è applicabile al mio problema esatto (ho ancora bisogno di altre macro all'interno di Z per espandere). Per quanto riguarda M4, beh, se fosse un progetto vero, sarei d'accordo, ma la sfida è farlo con il bizzarro preprocessore di C! Come domanda correlata, sarebbe possibile usare le parentesi di '()' invece di '[]'? cioè c'è un modo per sfuggire ai personaggi delle parentesi? – Dave

+0

Non capisco la tua domanda correlata. Stavo solo usando parentesi quadre per l'illustrazione; per quanto riguarda il preprocessore, non hanno alcun significato speciale. – zwol

+0

Intendo questa riga: '#define C (x, y) y x]/* nota parentesi quadra chiusa invece di parentesi chiusa * /' come a ')', si interrompe. – Dave

0

Riguarda l'ordine in cui le macro vengono elaborate. La soluzione più semplice è aggiungere parentesi aggiuntive attorno all'argomento Z.

printf(Z((A("1")("2") "3"))); 
+0

Ma sarebbe possibile fallo * senza * richiedendo una sintassi diversa quando usato? (possibilmente facendo 'Z' in una macro più complicata). – Dave

1

eclipse cdt è eccellente per eseguire il debug delle domande. per Eclipse, passa con il mouse su una macro per iniziare. qui è informazioni detialed su di esso:

C/C++ Software Development with Eclipse >> 2.1.7. Macro Expansion

per la seconda macro, eclisse mostra la seguente:

int main (void) { 
    printf(Z(A("1") ("2") "3")); 
} 

enter image description here enter image description here enter image description here enter image description here enter image description here

Spottin g l'errore

Avviso nell'espansione # 3 C ("2", "3" solo "scompare. Prendo questo come il modo in cui CDT dice "elenco argomenti non terminati". Qualunque sia il motivo per cui scompare, questo è il metodo che preferisco prendere durante il debug delle macro.

  • L'utilizzo di questo strumento rende chiaro che in Espansione n. 2 (terza immagine) abbiamo un insieme non terminato di parentesi, individuando quindi l'errore.

Comprendere una soluzione

Dopo armeggiare intorno a un po 'di questo strumento, credo che questo è ciò che si erano dopo:

printf(Z((A("1") ("2") "3"))); 

yields (using gcc -E main.c -c) 

printf(("1" "3" "2")); 

enter image description here