Макроси - це копіювані / вставлені фрагменти тексту, які попередній процесор помістить у справжній код; автор макросу сподівається, що заміна створить дійсний код.
Для досягнення успіху в цьому є три хороших "поради":
Допоможіть макросу поводитись як справжній код
Звичайний код зазвичай закінчується крапкою з двокрапкою. Якщо коду перегляду користувача не потрібен ...
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) .