Day 12.
Иногда вы хотите написать какой-то общий макрос, который будет работать для произвольного числа аргументов:
#define MACRO(...) __VA_ARGS__
__VA_ARGS__ — способ сослаться на все аргументы, обозначенные как ... в макросе. Компилятор просто перечислит их через запятую:
#define DEBUG(...) printString(__VA_ARGS__)
DEBUG("1", "2", "3"); // printString("1", "2", "3")
Частая проблема с
__VA_ARGS__ — пустой пак аргументов.
#define LOG(fmt, ...) print(fmt, __VA_ARGS__)
LOG("str"); // print("fmt", )
Мы получаем пустой пак аргументов, из-за чего получаем лишнюю запятую, после которой ничего нет. Это большая боль.
Как жить с этим, расскажу попозже.
У макросов часто возникают проблемы с шаблонами, так как параметры макроса сплитятся по запятой:
#define A(a, b) f(a, b)
A(std::pair<int, int>{1, 1}, 1);
И вы получите ошибку компиляции, т.к. в примере выше
A получил 4 аргумента:•
std::pair<int•
int>{1, •
1}•
1А может принять только 2 (и никак не
f(std::pair<int, int>{1) ).Я постоянно стукаюсь об это, когда пишу тесты с gtest.
С
__VA_ARGS__ это иногда может заработать, т.к. вы теперь все аргументы всегда передаёте пачкой:
#define A(...) f(__VA_ARGS__)
A(std::pair<int, int>{1, 1}, 1); // f(std::pair<int, int>{1, 1}, 1)
__VA_ARGS__ можно использовать для подсчёта кол-ва аргументов в макросе (пример для до 5 аргументов):
#define NARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
#define NARGS(...) NARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
NARGS(a, b, c) // 3
Раскрывается примерно так:
NARGS(a, b, c)
NARGS_IMPL(
_1 = a
_2 = b
_3 = c
_4 = 5
_5 = 4
N = 3
)
N = 3
@thisnotes. Patreon, Boosty.
Спасибо Artyom Garkavy и niki4smirn.