Макросы - это скопированные / вставленные фрагменты текста, которые препроцессор вставит в подлинный код; Автор макроса надеется, что замена даст действительный код.
В этом есть три хороших «совета»:
Помогите макросу вести себя как подлинный код
Обычный код обычно заканчивается точкой с запятой. Если пользователь просматривает код, который ему не нужен ...
doSomething(1) ;
DO_SOMETHING_ELSE(2) // <== Hey? What's this?
doSomethingElseAgain(3) ;
Это означает, что пользователь ожидает, что компилятор выдаст ошибку, если точка с запятой отсутствует.
Но по-настоящему веская причина в том, что когда-нибудь автору макроса, возможно, потребуется заменить макрос подлинной функцией (возможно, встроенной). Таким образом, макрос должен действительно вести себя как один.
Таким образом, у нас должен быть макрос, которому нужна точка с запятой.
Создайте действительный код
Как показано в ответе jfm3, иногда макрос содержит более одной инструкции. И если макрос используется внутри оператора if, это будет проблематично:
if(bIsOk)
MY_MACRO(42) ;
Этот макрос может быть расширен как:
#define MY_MACRO(x) f(x) ; g(x)
if(bIsOk)
f(42) ; g(42) ; // was MY_MACRO(42) ;
g
Функция будет выполнена независимо от значения bIsOk
.
Это означает, что мы должны добавить область действия в макрос:
#define MY_MACRO(x) { f(x) ; g(x) ; }
if(bIsOk)
{ f(42) ; g(42) ; } ; // was MY_MACRO(42) ;
Введите действительный код 2
Если макрос что-то вроде:
#define MY_MACRO(x) int i = x + 1 ; f(i) ;
У нас может быть другая проблема в следующем коде:
void doSomething()
{
int i = 25 ;
MY_MACRO(32) ;
}
Потому что это будет расширяться как:
void doSomething()
{
int i = 25 ;
int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}
Этот код не будет компилироваться, конечно. Итак, опять же, решение использует область:
#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) ;
}
Код снова ведет себя правильно.
Сочетание точек с запятой + эффектов
Существует одна идиома C / C ++, которая производит такой эффект: цикл do / while:
do
{
// code
}
while(false) ;
Do / while может создать область видимости, таким образом инкапсулируя код макроса, и, в конце концов, нуждается в точке с запятой, расширяя ее до кода, нуждающегося в нем.
Бонус?
Компилятор C ++ оптимизирует цикл do / while, так как факт его пост-условия ложен, известен во время компиляции. Это означает, что макрос, как:
#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.
}
будет правильно расширяться как
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.
}
а затем компилируется и оптимизируется как
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
{
f(43) ; g(43) ;
}
// Etc.
}
void
типа в конце ... like ((void) 0) .