Традиционный дебаг
Наверное, каждый спортивный программист хотя бы раз выводил содержимое контейнера, массива или просто значение переменной во время дебага. В этот момент появляются такие вот уродливые конструкции:
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) string(#x), string(": "), x, string(", ")
Она будет просто подставлять вместо только переменной ее имя, двоеточие, значение и запятую. Теперь можно написать функцию, которая будет вызывать f_dbg()
от каждого подставленного аргумента. Назовем такую функцию n_dbg()
. Нужно только писать _()
вокруг каждого аргумента, который вы ей передаете.
Сравните:
string a = "dfs"; pair<int, string> b = {123, "ksdf"};
auto sum = [](auto l, auto r){return l + r;};
cout << "a " << a << " | " << "b " << b.first << " " << b.second << " | " << "sum " << sum(3,4) << endl;
n_dbg(_(a), _(b), _(sum(3,4)));
традиционная версия выводит:
a dfs | b 123 ksdf | sum 7
n_dbg()
выводит:
[a: dfs, b: [123,ksdf], sum(3,4): 7, ]
/*-----------------------------------------------*/
Надеюсь, этот код сохранит немного вашего бесценного времени во время контеста.
P.S. Если вы захотите адаптировать код к c++11/14, я не возражаю, если вы склонируете этот блог для (c++11/14 эдишн). Или вы можете предложить ваш код, и я добавлю его сюда, чтобы все версии были вместе.
UPD1: добавлен вывод unsigned int, long long, unsigned long long, long double, double
UPD2: добавлен вывод bitset
UPD3: Немного изменен интерфейс и описание интерфейса функций
UPD4: добавлена функция n_dbg()
UPD5: оказалось, что n_dbg()
можно передавать функции