2010-04-04 10 views
7

Sto tentando di identificare un problema a causa di un utilizzo insolito di macro variadic. Ecco la macro ipotetica:il problema del diverso trattamento a __VA_ARGS__ quando si utilizza VS 2008 e GCC

#define va(c, d, ...) c(d, __VA_ARGS__) 
#define var(a, b, ...) va(__VA_ARGS__, a, b) 

var(2, 3, printf, “%d %d %d\n”, 1);

Per gcc, il preprocessore emette

printf("%d %d %d\n", 1, 2, 3)

ma per VS 2008, l'uscita è

printf, “%d %d %d\n”, 1(2, 3);

Ho il sospetto che la differenza è causata dal diverso trattamento a VA_ARGS, per gcc, prima espande l'espressione in va (printf, "% d% d% d \ n", 1, 2, 3) e tratta 1, 2, 3 come VA_ARGS per macro va. Ma per VS 2008, per prima cosa verrà trattato come VA_ARGS per macro va, quindi verrà eseguita l'espansione.

Qual è l'interpretazione corretta per macro variadic C99? o il mio utilizzo cade in un comportamento indefinito?

+1

stai cercando di risolvere realmente un problema, o stai semplicemente facendo un esercizio accademico/di ricerca? Ci sono modi per definire la macro var senza incorrere in questo problema. – Cheeso

+1

Provo a risolvere il problema. Uso intensamente questo metodo nel mio progetto, il che mi fa risparmiare un sacco di tempo: http://liuliu.me/eyes/lets-abusively-use-cpp-macros – liuliu

risposta

4

Vedere la ISO/IEC 9899: 1999, capitolo 6.10.3.1. Si precisa che:

Dopo gli argomenti per la chiamata di una macro funzione simile sono stati identificati , sostituzione argomento avviene. Un parametro nell'elenco di sostituzione, a meno che non sia preceduto da un token di preelaborazione # o ## o seguito da un token di preelaborazione ## (vedere sotto), viene sostituito dall'argomento corrispondente dopo. Tutte le macro in esso contenute sono state espanse. Prima che lo venga sostituito, i token di preelaborazione di ciascun argomento sono completamente sostituiti con la macro come se formassero il resto del file di pre-elaborazione; non sono disponibili altri token di preelaborazione.

Così va primo argomento c ha una preelaborazione gettone essendo __VA_ARGS__, che, secondo questo paragrafo, deve essere macro sostituito prima viene sostituito da c (che ancora non dà la risposta su quale compilatore è a destra)

Ma più tardi:

Un identificativo __VA_ARGS__ che si verifica nell'elenco sostituzione sono considerati come se fosse un parametro , e gli argomenti variabili ne costituiscono i token pre-elaborazione utilizzati per sostituirlo.

Secondo il primo frammento, prima gli argomenti var sono identificati. Sono: 2, 3, printf, “%d %d %d\n” e 1. Ora la sostituzione dell'argomento ha luogo. Ciò significa che i parametri dalla lista di sostituzione var vengono presi e sostituiti.Tuttavia, il secondo frammento indica che l'identificatore __VA_ARGS__ deve essere trattato come un parametro, quindi deve essere sostituito da printf, “%d %d %d\n”, 1. Ora non ci sono macro all'interno dei parametri, quindi non avviene alcuna ulteriore sostituzione, con conseguente espansione di var(2, 3, printf, “%d %d %d\n”, 1); in va(printf, “%d %d %d\n”, 1, 2, 3);. Perché va è una macro, è anche espansa, dando il risultato di printf(“%d %d %d\n”, 1, 2, 3);. Ora, se si prende il ragionamento VS 2008, come può identificare gli argomenti per va, se uno di loro è __VA_ARGS__ da var, e può contenere molti argomenti? Bene, tratta ,, che a mio avviso è sbagliato, poiché, secondo il frammento uno, la sostituzione argomento ha luogo solo dopo gli argomenti per l'invocazione sono stati identificati.

Così sembra a me, che in var il preprocessore deve in primo luogo identificare gli argomenti per l'invocazione della macro va, e quindi avviare l'espansione va. E questo significa che probabilmente gcc è proprio qui.

Viene anche mostrato in C preprocessor and concatenation in che modo i token di elaborazione vengono sostituiti in modo macro, fino a quando non vi sono identificatori che possono essere ulteriormente espansi come macro, oppure il preprocessore rileva una ricorsione e termina l'espansione.

+0

Grazie mille. La tua risposta è chiara e concisa. Mi sembra anche che la risposta di gcc abbia più senso. – liuliu

+1

Beh, il fatto che l'output di gcc fosse più sensato per un normale essere umano era quello con cui ho iniziato, ma spero che la risposta non sia stata di parte. A mio parere, lo standard dovrebbe essere più preciso sull'ordine esatto delle macro in espansione, specialmente quando entrano in gioco '__VA_ARGS__'. –

6

C'è un modo semplice per affrontare questo problema:

#define exp(...) __VA_ARGS__ 
#define va(c, d, ...) c(d, __VA_ARGS__) 
#define var(a, b, ...) exp(va(__VA_ARGS__, a, b)) 

var(2, 3, printf, “%d %d %d\n”, 1); 

questo farà il trucco su VS 2008 e non influenzerà gcc

+0

Incredibile. Grazie! – liuliu

Problemi correlati