Классика жанра же: когда делаешь макрос, его всегда, кроме случаев
когда невозможно, нужно делать выражением (а не оператором --
потому, что сам макрос могут вставить в контекст выражения, а не
оператора), и обязательно брать в скобки тело макроса! И аргументы
макроса тоже нужно брать в скобки, потому, что в общем случае
неизвестно с каким аргументами вызывали макрос, может там
MACRO(a+b, a*b), и неизвестно как оно состыкуется с операторами в
макросе. Пример:
#define MACRO(x, y) ((x)+(y))
Кроме того, если в макрос передаются аргументы-переменные например (любые идентификаторы или типы на самом деле), они могут конфликтовать с переменными или типами продекларированными в теле макроса (это если макрос таки не выражение, а оператор, или если макрос на C++ и содержит лямбду, или если макрос на GCC и содержит statement expressions). Поэтому декларировать что-то внутри макроса лучше с какими-нибудь префиксами/суффиксами, а не просто A или Б.
Пример:
#define MACRO(count, f) do {
int n__ = count; \
do f(n__) while (--n__); \
} while(0)
// MACRO(n__,f) gives an error because of name clashing.
Здесь так же показано, что макросы являющиеся оператором стоит обязательно включить в блок кода (фигурные скобки), который может быть внутри "do {} while(0)", что позволит в конце макроса вставить точку с запятой. Если без do/while то в таком варианте будет ошибка:
int g(void);
int f(void);
// this gives an error:
#define MACRO() { g(); }
// this compiles without error
//#define MACRO() do { g(); } while(0)
int main(int ac, char *av[])
{
(void)av;
if (ac)
MACRO();
else
f();
return 0;
}
main.cpp:17:5: error: 'else' without a previous 'if'
17 | else
| ^~~~
https://coliru.stacked-crooked.com/a/6fee23d2ad027e9e
Пример макроса для GCC:
#define MACRO(n, f) ({ \
int n__ = n; \
do f(n__) while (--n__); \
})
Последний макрос можно использовать внутри выражения, а не вместо оператора -- иногда это жизненно необходимо. И этот макрос может вернуть значение (равное значению последнему выражению в фигурных скобках). Ну в gcc ещё в теле такого макроса можно функцию определить и вызвать её, только указатель на такую функцию не стоит возвращать из макроса -- программа навернётся. И такой код, если он адресует внешние переменные (по отношению к функции/макросу) не будет работать на системах с неисполняемым стеком (где GCC размещает свой трамплин). Если не прав, то прошу поправить.
Чтобы избежать дальнейших конфликтов имён обычно придерживаются некого code style, например:
1) макросы именуются строго заглавными буквами: #define MACRO(x, y) ((x)+(y))
2) типы обозначаются в CamelCase (PascalStyle): typedef struct S { ... } MyType;
3) объекты именуются в snake_case или в lowerCamelCase : MyType my_object или MyType myObject;
4) функции именуются в snake_case или lowerCamelCase тоже (функцию легко перепутать с конструктором типа в C++ обычно...);
См. также: http://caxapa.ru/910140
Примеры сложных макросов:
http://caxapa.ru/964034
http://caxapa.ru/964114
Расширения GCC:
https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html