6 Stimmen

Unerwünschte C-Präprozessor-Makro-Erweiterung

Ich verwende ein Unit-Test-Framework, das sich auf eine REQUIRE Makro zur Durchführung von Assertions.

Vereinfacht ausgedrückt funktioniert das Makro wie folgt:

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

Die Definition ist ähnlich wie diese:

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

PerformAssertion sind die ersten beiden Parameter vom Typ: const char* . Der Grund für den zweiten Parameter ( #expr ) ist, damit der genaue Ausdruck, der geltend gemacht wurde, protokolliert werden kann. Genau hier liegt das Problem. Der Präprozessor expandiert den Ausdruck, bevor er als const char * Es handelt sich also nicht um denselben Ausdruck, der ursprünglich behauptet wurde.

Zum Beispiel:

REQUIRE( foo != NULL );

würde zu diesem Anruf führen:

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

Wie Sie sehen können, wird der Ausdruck teilweise expandiert, z. B. der Ausdruck foo != NULL erscheint im Protokoll als foo != 0 . Die NULL (das ist ein Makro, das so definiert ist, dass es 0 ) wurde vom C-Präprozessor erweitert, bevor der Nachrichtentext der Assertions erstellt wurde. Gibt es eine Möglichkeit, die Erweiterung für den Meldungstext zu ignorieren oder zu umgehen?

EDIT: Hier ist die Lösung, für alle, die es interessiert:

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

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

5voto

Dani Punkte 28963

Versuchen Sie, das Stringifying vor dem Aufruf der internen Anforderung durchzuführen. Ihr Problem ist, dass es in der zweiten Erweiterung, die NULL expandiert, an das interne require übergeben wird. Wenn Sie das Stringifying davor machen, z.B. im require-Makro, wird die NULL nicht expandiert.

2voto

Sergey Kalinichenko Punkte 694383

Hier ist, was los ist: da Sie Makro, wo die "stringization" Operator # angewendet wird, funktioniert die Abfolge der Vorgänge wie folgt:

  • Der Präprozessor identifiziert die Argumente von REQUIRE(NULL) und führt aus Argumentensubstitution gemäß C 6.10.3.1. Zu diesem Zeitpunkt sieht die Ersetzung wie folgt aus INTERNAL_REQUIRE( 0, "REQUIRE" ) denn NULL wird erweitert zu 0 .
  • Der Präprozessor setzt die Erweiterung der Makrokette mit INTERNAL_REQUIRE An dieser Stelle wird die Tatsache, dass das Makro mit NULL geht verloren: Für den Präprozessor ist der Ausdruck, der an INTERNAL_REQUIRE es 0 .

Ein Schlüssel zur Lösung dieses Problems findet sich in diesem Absatz der Norm:

Ein Parameter in der Ersetzungsliste wird durch das entsprechende Argument ersetzt, nachdem alle darin enthaltenen Makros expandiert wurden, es sei denn, vor ihm steht ein #- oder ##-Vorverarbeitungs-Token oder es folgt ein ##-Vorverarbeitungs-Token (siehe unten).

Das bedeutet, dass Sie, wenn Sie den genauen Ausdruck erfassen möchten, dies in der allerersten Ebene der Makroerweiterung tun müssen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X