Первый рабочий код:
#include <iostream>
// NOTE: requires compiler which has __PRETTY_FUNCTION__, like gcc or clang
#define DUMP(v) std::cerr << __PRETTY_FUNCTION__ << ':' << __LINE__ << ':' << #v << "='" << (v) << "'\n"
int main(int argc) {
DUMP(argc);
DUMP(argc+1);
return 0;
}
который дает вывод stderr (gcc):
int main(int):8:argc='1'
int main(int):9:argc+1='2'
Теперь я хотел бы иметь VARIADIC макрос с любым количеством аргументов, например,
DUMP(argc, argc+1);
будет работать, с выводом, как:
int main(int):8:argc='1',argc+1='2'
Но я еще не мог придумать хорошее решение. Возможно ли это с помощью макроса? Если нет, то как насчет шаблонов или комбинации макросов и шаблонов? C ++ 11 и Boost в порядке, если необходимо, и решение может быть НКУ-конкретный, если нет стандартного способа. Ищем реальный код, который заставляет вариационный DUMP работать, как описано выше, или, по крайней мере, отображать эквивалентную информацию.
Хорошо. Это НЕ ХОРОШО. Но это работает, до заданного количества аргументов (я ленился в 4). Он сильно сорван с здесь (серьезно, нажмите и upvote). Он использует прием буквального расширения аргументов перед заранее написанным списком, вытягивая правильное число из фиксированной позиции сзади. Затем он вызывает правильное имя макроса, добавляя номер. Очистить? Нету! Вот код:
#include <iostream>
// Pretty normal macro tricks
#define STRINGIZE(a) STRINGIZE1(a)
#define STRINGIZE1(a) STRINGIZE2(a)
#define STRINGIZE2(a) #a
#define CONCAT(a, b) CONCAT1(a, b)
#define CONCAT1(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a##b
// Faux-recursively dump all the arguments
#define DUMP_1(first) std::cout << STRINGIZE(first) << "='" << first << "'\n";
#define DUMP_2(first, ...) \
do {\
std::cout << STRINGIZE(first) << "='" << first << "': ";\
DUMP_1(__VA_ARGS__);\
} while (false)
#define DUMP_3(first, ...) \
do {\
std::cout << STRINGIZE(first) << "='" << first << "': ";\
DUMP_2(__VA_ARGS__);\
} while (false)
#define DUMP_4(first, ...) \
do {\
std::cout << STRINGIZE(first) << "='" << first << "': ";\
DUMP_3(__VA_ARGS__);\
} while (false)// Count the arguments
// Construct the forward/backward list:
#define COUNT_ARGS(...) COUNT_ARGS_(__VA_ARGS__, RSEQ())
// Forward the list on (macro pain):
#define COUNT_ARGS_(...) COUNT_ARGS_N(__VA_ARGS__)
// The n+1th element is the count (predetermined to support up to n)
#define COUNT_ARGS_N(_1, _2, _3, _4, N, ...) N
#define RSEQ() 4, 3, 2, 1// This just calls the correct DUMP_#
#define DUMP_(N, ...) CONCAT(DUMP_, N)(__VA_ARGS__)
// Start the line, and start the "recursion"#define DUMP(...) \
do {\
std::cout << __PRETTY_FUNCTION__ << ':' << __LINE__ << ": "; \
DUMP_(COUNT_ARGS(__VA_ARGS__), __VA_ARGS__); \
} while (false)
int main(int argc, char* argv[])
{
DUMP(argc);
int i = 10;
const char str[] = "Hello, world";
DUMP(i, str);
return 0;
}
Выход:
$ ./a.out
int main(int, char**):49: argc='1'
int main(int, char**):52: i='10': str='Hello, world'
Редактировать:
Вот простая версия счетной части (сложная часть):
#include <iostream>
#define COUNT_ARGS(...) COUNT_ARGS_(__VA_ARGS__, RSEQ())
#define COUNT_ARGS_(...) COUNT_ARGS_N(__VA_ARGS__)
#define COUNT_ARGS_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define RSEQ() 6, 5, 4, 3, 2, 1
int main()
{
std::cout << COUNT_ARGS(a, b) << '\n';
std::cout << COUNT_ARGS(a, b, c, d) << '\n';
return 0;
}
Выход:
1
2
Как это работает?
Хорошо, скажи, что ты настроил это на счет до 6, как и я. Он создает длинный список и выполняет 7-й элемент. Это всегда содержит правильное значение, потому что список, из которого оно извлекает это значение, строится путем помещения всех аргументов, предоставленных пользователем, с последующим подсчетом задом наперед.
Таким образом, добавив еще один аргумент, вы нажимаете обратный отсчет вдоль 1 и получаете выше номер из него. Посмотри на эту картину:
С помощью Boost.PP, Вы можете написать это:
#define DUMP_EACH(r, data, v) std::cerr << __PRETTY_FUNCTION__ << ':' << __LINE__ << ':' << #v << "='" << (v) << "'\n"#define DUMP(...) BOOST_PP_SEQ_FOR_EACH(DUMP_EACH, ~, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
Вам также нужно будет добавить -DBOOST_PP_VARIADICS=1
когда вы компилируете. Если вы не хотите использовать повышение, вы можете написать простую FOR_EACH
макрос для перебора каждого аргумента, например так:
/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)
/* This will call a macro on each argument passed in */
#define FOR_EACH(macro, ...) CAT(FOR_EACH_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define FOR_EACH_1(m, x1) m(x1)
#define FOR_EACH_2(m, x1, x2) m(x1) m(x2)
#define FOR_EACH_3(m, x1, x2, x3) m(x1) m(x2) m(x3)
#define FOR_EACH_4(m, x1, x2, x3, x4) m(x1) m(x2) m(x3) m(x4)
#define FOR_EACH_5(m, x1, x2, x3, x4, x5) m(x1) m(x2) m(x3) m(x4) m(x5)
#define FOR_EACH_6(m, x1, x2, x3, x4, x5, x6) m(x1) m(x2) m(x3) m(x4) m(x5) m(x6)
#define FOR_EACH_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1) m(x2) m(x3) m(x4) m(x5) m(x6) m(x7)
#define FOR_EACH_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1) m(x2) m(x3) m(x4) m(x5) m(x6) m(x7) m(x8)
Затем вы можете определить свой макрос дампа следующим образом:
#define DUMP_EACH(v) std::cerr << __PRETTY_FUNCTION__ << ':' << __LINE__ << ':' << #v << "='" << (v) << "'\n"#define DUMP(...) FOR_EACH(DUMP_EACH, __VA_ARGS__)