2012-06-19 28 views
6

Utilizzo un framework di test unità che si basa su una macro REQUIRE per l'esecuzione di asserzioni.Espansione macro preprocessorico C preprocessor

semplificato, le macro funziona come questo:

#define REQUIRE(expr) INTERNAL_REQUIRE(expr, "REQUIRE") 

che è definito simile a questo:

#define INTERNAL_REQUIRE(expr, macroName) \ 
PerformAssertion(macroName, #expr, expr); 

PerformAssertion 's primi due parametri sono del tipo: const char*. Il motivo del secondo parametro (#expr) è quindi l'espressione esatta che è stata asserita può essere registrata. Questo è dove si trova il problema. Il preprocessore espande l'espressione prima che venga passata come const char *, quindi non è la stessa espressione che è stata originariamente asserita.

Ad esempio:

REQUIRE(foo != NULL); 

comporterebbe questa chiamata:

PerformAssertion("REQUIRE", "foo != 0", foo != 0); 

Come si può vedere, l'espressione è parzialmente espansa, ad esempio l'espressione foo != NULL viene visualizzata nel registro come foo != 0. Lo NULL (che è una macro definita per essere 0) è stato espanso dal preprocessore C prima di creare il testo del messaggio delle asserzioni. C'è un modo in cui posso ignorare o bypassare l'espansione per il testo del messaggio?

EDIT: Ecco la soluzione, per chiunque sia curioso:

#define REQUIRE(expr) INTERNAL_REQUIRE(expr, #expr, "REQUIRE") 

#define INTERNAL_REQUIRE(expr, exprString, macroName) \ 
PerformAssertion(macroName, exprString, expr); 
+2

Basta usare 'INTERNAL_REQUIRE (expr, #expr," REQUIRE ")' invece del modulo a due argomenti? – kennytm

+1

Sì, lo farò. – Jeff

risposta

5

provare a fare lo stringifying prima della chiamata a richiedono l'interno. Il tuo problema è che è passato alla richiesta interna nella seconda espansione che espande NULL. Se fai in modo che la stringificazione avvenga prima, ad es. Nella macro richiesta, non espanderà il NULL.

+0

Ha funzionato perfettamente, grazie! Ti andrebbe di spiegare come ha funzionato un po 'più in dettaglio? – Jeff

2

Ecco che cosa sta succedendo: dal momento che la macro in cui l'operatore "stringization" # viene applicato è di secondo livello, la sequenza delle operazioni funziona come segue:

  • preprocessore identifica gli argomenti di REQUIRE(NULL) ed esegue argument substitution come da C 6.10.3.1. A questo punto, la sostituzione sembra INTERNAL_REQUIRE(0, "REQUIRE"), perché NULL viene espanso come 0.
  • Il preprocessore continua ad espandere la catena macro con INTERNAL_REQUIRE; a questo punto, il fatto che la macro sia stata chiamata con NULL viene perso: per quanto riguarda il preprocessore, l'espressione passata a INTERNAL_REQUIRE è 0.

Una chiave per risolvere questo problema è in questo paragrafo dalla norma:

Un parametro nell'elenco sostituzione, a meno che non preceduto da un # o ## pre-elaborazione di token o seguita da una pre-elaborazione ## token (vedi sotto), viene sostituito dall'argomento corrispondente dopo che tutte le macro in esso contenute sono state espanse.

Ciò significa che se si desidera acquisire l'espressione esatta, è necessario farlo nel primissimo livello dell'espansione macro.

+0

Grazie, che ha soddisfatto la mia curiosità e spiega perché la soluzione di Dani funziona. – Jeff