Традиционный дебаг
Наверное, каждый спортивный программист хотя бы раз выводил содержимое контейнера, массива или просто значение переменной во время дебага. В этот момент появляются такие вот уродливые конструкции:
vector<int> a[4][6] = {{{2,3}, {4}}, {{6,2}, {4,5}}};
for(int i = 0; i < size(a); ++i, cout << endl){
for(int j = 0; j < size(a[i]); ++j, cout << endl){
for(int k : a[i][j]){
cout << k << ' ';
}
}
}
О, господи, да еще и гарантированная проверка, что счетчик не выходит за пределы массива! 2D, 3D, какая разница, если это нелья записать кратко?! Да еще эти endlы вместо пустых ячеек...
Дебаг с f_dbg()
Мне надоело бесконечно набивать эти циклы, поэтому я написал несколько строчек, которые могут напечатать значения практически чего угодно, хранящего встроенные типы. Вывод сделан в стиле питона. Конечно, у этого кода есть некоторые ограничения, но в целом он мне нравится.
Вы можете найти его здесь. Он использует некоторые фичи c++17, поэтому выберите правильный компилятор.
Компактная версия сгенерирована из полной на сайте http://removelinebreaks.net/
Как работает форматированный вывод
Назовем массивом c-массив, вектор, дек, array. Тогда f_dbg()
может вывести подмассив. Чтобы она сделала это, вы передаете ей по [две закрытые границы] для каждого измерения, причем можно опустить несколько последних границ. Если они слишком большие, f_dbg()
уменьшает их, чтобы они были внутри подмассива. По умолчанию начальная и конечная граница для каждого измерения устанавливаются на начало и конец каждого измерения.
Если тип элементов в массиве тоже какой-то массив, f_dbg()
вызывается рекурсивно от этого элемента, пока не достигнет элементарного типа — int, char, double и т.д. Это значение выводится, и все, собственно.
Другие структуры данных вроде map, set не имеют индексов, и поэтому выводятся полностью: от начала до конца. В map f_dbg()
вызывается и от ключа, и от значения.
Пары выводятся так же: рекурсивно вызывается f_dbg()
от первого элемента, потом от второго.
/*-----------------------------------------------*/
Сравните:
// традиционная версия(изменена для похожести на f_dbg())
int x1 = 0;
int x2 = size(a)-1;
for(int i = x1; i <= x2; ++i, cout << endl){
int y1 = 0;
int y2 = size(a[i])-1;
for(int j = y1; j <= y2; ++j, cout << endl){
for(int k : a[i][j]){
cout << k << ' ';
}
}
}
// f_dbg()
f_dbg(a,0,1);
традиционная версия выводит:
2 3
4
6 2
4 5
/*...endls...*/
f_dbg()
выводит:
[[[2,3],[4],[]]
[[6,2],[4,5],[]]]
/*-----------------------------------------------*/
Дебаг с n_dbg()
Вы когда-нибудь мечтали о том, чтобы выводить названия скольки угодно переменных и их значения без этих << "my_var" << my_var
?
Взгляните на named debug
Можно использовать макрофункцию:
#define _(x) #x, ": ", x, ", "
Она будет просто подставлять вместо аргумента его имя, двоеточие, значение и запятую. Теперь можно написать функцию n_dbg()
, которая будет вызывать f_dbg()
от каждого аргумента. Тогда если вы хотите получить больше инфы об аргументе в выводе, вы записываете его внутри _()
, если просто значение — пишете название_аргумента
запятая ", "
(чтобы отделить от следующего аргумента). По умолчанию после такого блока вывода печатается endl.
Сравните:
string t = "dfs"; pair<int, string> u = {123, "ksdf"};
auto v = [](int l, int r){return l + r;};
cout << t << "| " << "u " << u.first << " " << u.second << "| " << "v " << v(3,4) << "| " << v(3,4) << endl;
n_dbg(t, ", ", _(u), _(v(3,4)), v(3,4));
традиционная версия выводит:
dfs| u 123 ksdf| v 7| 7
n_dbg()
выводит:
[dfs, u: [123,ksdf], v(3,4): 7, 7]
/*-----------------------------------------------*/
Надеюсь, этот код сохранит немного вашего бесценного времени на контестах.
P.S. Если вы захотите адаптировать код к c++11/14, я не возражаю, если вы склонируете этот блог для (c++11/14 эдишн). Или вы можете предложить ваш код, и я добавлю его сюда, чтобы все версии были вместе.
UPD1 В n_dbg()
добавлен пример обычного вывода аргумента, вывода функции
UPD2 В ссылке в блоке для копирования названия дебаг-функций помещены в начало блока, чтобы не забыть их названия
UPD3 Добавлен вывод const char*