Day 21.
Вернёмся к
__VA_ARGS__. Мы смотрели на такой макрос:
#define LOG(fmt, ...) print(fmt, __VA_ARGS__)
И когда мы используем его с одним аргументом:
LOG("str");
мы получим
print("fmt", ), что вообще-то невалидно, т.к. одна запятая без аргумента сзади это CE.Это решали расширением компиляторов:
#define LOG(fmt, ...) print(fmt, ##__VA_ARGS__)
То есть оно как бы работает, но не везде одинаково, есть проблемы с разными опциями компиляции.
Стандартное решение (с C++20) — использование
__VA_OPT__:
#define LOG(fmt, ...) print(fmt __VA_OPT__(,) __VA_ARGS__)
__VA_OPT__ — это такая приколюха, которая оставляет аргумент, если __VA_ARGS__ в том же макросе не пустые. Если пустые (что? да), не оставляет. Совать в аргумент можно не только запятую:
#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })
SDEF(foo); // replaced by S foo;
SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 };
Хотя с
__VA_ARGS__ внутри __VA_OPT__ надо быть аккуратными. Есть классическая статья David Mazières из Stanford про то, как можно абюзить
__VA_OPT__ для создания рекурсивных макросов: https://www.scs.stanford.edu/~dm/blog/va-opt.html@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.