Expansionsregeln beim C++-Präprozessor
-
Hallo.
Ich habe immer noch ernsthafte Probleme damit, den C++-Präprozessor zu verstehen. Ich versuche das folgende Codebeispiel lauffähig zu machen:
#define If_Helper_0(True) #define If_Helper_1(True) True #define If(Bit, True) If_Helper_ ## Bit (True) #define Id2(Arg1, Arg2) Arg1, Arg2 int main() { If(1, Id2(int a, b)); a = b = 42; }
Wieso dieses Listing nicht funktioniert, das folgende jedoch schon, ist mir schleierhaft.
#define Id1(Arg) Arg #define Id2(Arg1, Arg2) Arg1, Arg2 int main() { Id1(Id2(int a, b)); a = b = 42; }
Gruss.
PS.: Bitte nicht stumpf Boost.PP vorschlagen, ich möchte es ja auch verstehen.
Edit: Die Lösung sollte in C++03 funktionieren.
-
Erstmal ein möglicher Fix:
#define If_Helper_0(...) __VA_ARGS__ #define If_Helper_1(...) __VA_ARGS__ #define If(Bit, True) If_Helper_ ## Bit (True) // oder #define If(Bit, True) If_Helper_ ## Bit ((True)) // das führt aber zu einem Klammerpaar um das Resultat #define Id2(Arg1, Arg2) Arg1, Arg2 int main() { If(1, Id2(int a, b)); // expandiert zu int a, b a = b = 42; }
Ich glaube das hat irgendwas mit dem Argument Prescan zu tun, aber der Preprozessor ist auch nicht meine Stärke
-
Relativ einfach - du verwendest
If(Bit,True)
mitIf(1,Id2(int a,b))
.
Aber was ist Id2? Doch nur eine Ersetzung der zwei übergebenen Argumente.Nach der ersten Ersetzung hast du dann
If(1,int a,b)
stehen. Jetzt ersetzen wirIf
, sodassIf_Helper_1(int a,b)
rauskommt. AberIf_Helper_1
übernimmt nur ein Argument, nicht zwei. Und da kommt der Präprozessor ins Schleudern.Möglicher Fix wurde schon gepostet.
-
Ups, sorry. Wieder einmal vergessen zu erwähnen, dass ich bei C++03 bleiben möchte. Hab's im OP hinzugefügt.
Tim06TR schrieb:
// oder #define If(Bit, True) If_Helper_ ## Bit ((True)) // das führt aber zu einem Klammerpaar um das Resultat
Das Problem hierbei ist, dass das Klammerpaar bei beispielsweise (Template-)Parameterlisten nicht akzeptiert wird. Und das Makro zum Entfernen des Klammernpaares weist (unter C++03) die selbe Problematik auf.
dachschaden schrieb:
Relativ einfach - du verwendest
If(Bit,True)
mitIf(1,Id2(int a,b))
.
Aber was ist Id2? Doch nur eine Ersetzung der zwei übergebenen Argumente.Nach der ersten Ersetzung hast du dann
If(1,int a,b)
stehen. Jetzt ersetzen wirIf
, sodassIf_Helper_1(int a,b)
rauskommt. AberIf_Helper_1
übernimmt nur ein Argument, nicht zwei.Und wieso funktioniert es dann bei
If
wenn daId2
zuerst ersetzt wird? Dann würde man ja drei statt zwei Argumente übergeben. Und wieso funktioniert es beiId1
wenn zuvor ja angeblichId2
ersetzt wird?
-
[cpp.subst]/1 schrieb:
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a
#
or##
preprocessing token or
followed by a##
preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.Die Argumentsubstitution geschieht erst in der replacement list für ein Funktionsmakro. Nachdem die Konkatenierung durchgeführt wurde werden alle Argumente für die Parameter ersetzt.
#define If(Bit, True) If_Helper_ ## Bit (True)
An
If_Helper_*
wird tatsächlich makro-freier Text übergeben. Daher werden's auch drei Argumente.Id1(Id2(int a, b))
Id1
wird mitId2(..)
als Argument aufgerufen. Keine Kommas außerhalb des äußersten Klammerpaars, also nur ein Argument.
Id2(int a, b)
wird nicht sofort expandiert da es ein Argument ist.