Makros sind kopierte / eingefügte Textteile, die der Vorprozessor in den Originalcode einfügt. Der Autor des Makros hofft, dass der Ersatz einen gültigen Code erzeugt.
Es gibt drei gute "Tipps", um dies zu erreichen:
Helfen Sie dem Makro, sich wie echter Code zu verhalten
Normaler Code wird normalerweise durch ein Semikolon beendet. Sollte der Benutzer den Ansichtscode nicht benötigen ...
doSomething(1) ;
DO_SOMETHING_ELSE(2) // <== Hey? What's this?
doSomethingElseAgain(3) ;
Dies bedeutet, dass der Benutzer erwartet, dass der Compiler einen Fehler erzeugt, wenn das Semikolon fehlt.
Der wirklich gute Grund ist jedoch, dass der Autor des Makros das Makro möglicherweise irgendwann durch eine echte Funktion ersetzen muss (möglicherweise inline). Das Makro sollte sich also wirklich wie eines verhalten.
Wir sollten also ein Makro haben, das ein Semikolon benötigt.
Erstellen Sie einen gültigen Code
Wie in der Antwort von jfm3 gezeigt, enthält das Makro manchmal mehr als eine Anweisung. Und wenn das Makro in einer if-Anweisung verwendet wird, ist dies problematisch:
if(bIsOk)
MY_MACRO(42) ;
Dieses Makro könnte erweitert werden als:
#define MY_MACRO(x) f(x) ; g(x)
if(bIsOk)
f(42) ; g(42) ; // was MY_MACRO(42) ;
Die g
Funktion wird unabhängig vom Wert von ausgeführt bIsOk
.
Dies bedeutet, dass wir dem Makro einen Bereich hinzufügen müssen:
#define MY_MACRO(x) { f(x) ; g(x) ; }
if(bIsOk)
{ f(42) ; g(42) ; } ; // was MY_MACRO(42) ;
Erstellen Sie einen gültigen Code 2
Wenn das Makro so etwas wie:
#define MY_MACRO(x) int i = x + 1 ; f(i) ;
Wir könnten ein anderes Problem im folgenden Code haben:
void doSomething()
{
int i = 25 ;
MY_MACRO(32) ;
}
Weil es sich erweitern würde als:
void doSomething()
{
int i = 25 ;
int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}
Dieser Code wird natürlich nicht kompiliert. Die Lösung verwendet also wieder einen Bereich:
#define MY_MACRO(x) { int i = x + 1 ; f(i) ; }
void doSomething()
{
int i = 25 ;
{ int i = 32 + 1 ; f(i) ; } ; // was MY_MACRO(32) ;
}
Der Code verhält sich wieder korrekt.
Semikolon + Scope-Effekte kombinieren?
Es gibt ein C / C ++ - Idiom, das diesen Effekt erzeugt: Die do / while-Schleife:
do
{
// code
}
while(false) ;
Das do / while kann einen Bereich erstellen, wodurch der Code des Makros gekapselt wird, und benötigt am Ende ein Semikolon, wodurch es zu Code erweitert wird, der einen benötigt.
Der Bonus?
Der C ++ - Compiler optimiert die do / while-Schleife, da die Tatsache, dass ihre Nachbedingung falsch ist, zur Kompilierungszeit bekannt ist. Dies bedeutet, dass ein Makro wie folgt:
#define MY_MACRO(x) \
do \
{ \
const int i = x + 1 ; \
f(i) ; g(i) ; \
} \
while(false)
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
MY_MACRO(42) ;
// Etc.
}
wird korrekt erweitert als
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
do
{
const int i = 42 + 1 ; // was MY_MACRO(42) ;
f(i) ; g(i) ;
}
while(false) ;
// Etc.
}
und wird dann als kompiliert und optimiert
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
{
f(43) ; g(43) ;
}
// Etc.
}
void
am Ende einen Ausdruck vom Typ hinzufügen ... wie ((void) 0) .