Macros are copy/pasted pieces of text the pre-processor will put in the genuine code; the macro's author hopes the replacement will produce valid code.
There are three good "tips" to succeed in that:
Help the macro behave like genuine code
Normal code is usually ended by a semi-colon. Should the user view code not needing one...
doSomething(1) ;
DO_SOMETHING_ELSE(2) // <== Hey? What's this?
doSomethingElseAgain(3) ;
This means the user expects the compiler to produce an error if the semi-colon is absent.
But the real real good reason is that at some time, the macro's author will perhaps need to replace the macro with a genuine function (perhaps inlined). So the macro should really behave like one.
So we should have a macro needing semi-colon.
Produce a valid code
As shown in jfm3's answer, sometimes the macro contains more than one instruction. And if the macro is used inside a if statement, this will be problematic:
if(bIsOk)
MY_MACRO(42) ;
This macro could be expanded as:
#define MY_MACRO(x) f(x) ; g(x)
if(bIsOk)
f(42) ; g(42) ; // was MY_MACRO(42) ;
The g
function will be executed regardless of the value of bIsOk
.
This means that we must have to add a scope to the macro:
#define MY_MACRO(x) { f(x) ; g(x) ; }
if(bIsOk)
{ f(42) ; g(42) ; } ; // was MY_MACRO(42) ;
Produce a valid code 2
If the macro is something like:
#define MY_MACRO(x) int i = x + 1 ; f(i) ;
We could have another problem in the following code:
void doSomething()
{
int i = 25 ;
MY_MACRO(32) ;
}
Because it would expand as:
void doSomething()
{
int i = 25 ;
int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}
This code won't compile, of course. So, again, the solution is using a scope:
#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) ;
}
The code behaves correctly again.
Combining semi-colon + scope effects?
There is one C/C++ idiom that produces this effect: The do/while loop:
do
{
// code
}
while(false) ;
The do/while can create a scope, thus encapsulating the macro's code, and needs a semi-colon in the end, thus expanding into code needing one.
The bonus?
The C++ compiler will optimize away the do/while loop, as the fact its post-condition is false is known at compile time. This means that a macro like:
#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.
}
will expand correctly as
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.
}
and is then compiled and optimized away as
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
{
f(43) ; g(43) ;
}
// Etc.
}
void
type at the end... like ((void)0). – Phil1970do while
construct isn't compatible with return statements, so theif (1) { ... } else ((void)0)
construct has more compatible usages in Standard C. And in GNU C, you'll prefer the construct described in my answer. – Cœur