2012-06-19 14 views
6

Korzystam z platformy testów jednostkowych, która polega na makrze REQUIRE do wykonywania asercji.Niepożądane preprocesorowe wstępne przetwarzanie C

uproszczonej makro działa tak:

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

który jest zdefiniowany podobny do tego:

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

PerformAssertion „s Pierwsze dwa parametry są typu: const char*. Powód drugiego parametru (#expr) jest taki, że można potwierdzić dokładne wyrażenie, które zostało potwierdzone. Tutaj leży kwestia. Preprocesor rozszerza wyrażenie, zanim zostanie przekazane jako const char *, więc nie jest to to samo wyrażenie, które zostało pierwotnie zadeklarowane.

Na przykład:

REQUIRE(foo != NULL); 

spowodowałoby to wezwanie:

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

Jak widać, wyrażenie jest częściowo rozwinięty, np wyrażenie foo != NULL pojawia się w dzienniku jako foo != 0. Obiekt NULL (który jest makrem zdefiniowanym jako 0) został rozwinięty przez preprocesor C przed utworzeniem tekstu wiadomości potwierdzenia. Czy istnieje sposób, w jaki mogę zignorować lub pominąć rozszerzenie tekstu wiadomości?

EDIT: Oto rozwiązanie dla każdego, ciekawy:

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

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

Po prostu użyj 'INTERNAL_REQUIRE (expr, #expr," REQUIRE ")' zamiast formularza dwuargumentowego? – kennytm

+1

Tak, to wystarczy. – Jeff

Odpowiedz

5

Spróbuj dokonywania stringifying przed wywołaniem do wewnętrznego wymaga. Twoim problemem jest to, że jest przekazywane do wewnętrznego wymagania w drugim rozszerzeniu, które rozszerza NULL. Jeśli zmodyfikujesz wcześniej, np. W makrze "Wymagaj" nie rozwinie wartości NULL.

+0

Działa doskonale, dzięki! Czy chciałbyś wyjaśnić, jak to działało w trochę więcej szczegółów? – Jeff

2

Oto co się dzieje: skoro makro gdzie operator „stringization” # nanosi się na drugim poziomie, kolejność operacji działa następująco:

  • Preprocessor identyfikuje Argumenty REQUIRE(NULL) i wykonuje argument substitution zgodnie z C 6.10.3.1. W tym momencie zamiennik wygląda na INTERNAL_REQUIRE(0, "REQUIRE"), ponieważ NULL jest rozszerzony pod nazwą 0.
  • Preprocesor kontynuuje rozszerzanie łańcucha makr za pomocą INTERNAL_REQUIRE; w tym momencie utracono fakt, że makro zostało wywołane z NULL: w odniesieniu do preprocesora wyrażenie przekazane do INTERNAL_REQUIRE to 0.

Kluczem do rozwiązania tego problemu jest w niniejszym ustępie od normy:

parametr na liście zastępczej, chyba że poprzedzone # lub ## przerób tokena lub następuje wyprzedzającym ## token (patrz niżej) zostaje zastąpiony odpowiednim argumentem po rozszerzeniu wszystkich zawartych w nim makr.

Oznacza to, że jeśli chcesz uchwycić dokładne wyrażenie, musisz zrobić to na samym pierwszym poziomie makra.

+0

Dziękuję, to zaspokoiło moją ciekawość i wyjaśnia, dlaczego rozwiązanie Dani działa. – Jeff

Powiązane problemy