Как сделать так, чтобы макрос отладки с дампами переменных был переменным?

Первый рабочий код:

#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 работать, как описано выше, или, по крайней мере, отображать эквивалентную информацию.

2

Решение

Хорошо. Это НЕ ХОРОШО. Но это работает, до заданного количества аргументов (я ленился в 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 и получаете выше номер из него. Посмотри на эту картину:
введите описание изображения здесь

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__)
3

По вопросам рекламы [email protected]