Miscellany / Debugging
8.3 Debugging
Local-only debug printing for contest code. dbg, pr, dbg_rows, and pr_rows expand to no-ops unless LOCAL is defined (e.g. via the compiler flag -DLOCAL), so they can remain in submitted code without producing output.
dbg_repr(x)returns a string representation of arithmetic types, strings, pairs, tuples, iterable containers, and custom types that support insertion intostd::ostream.dbg(a, b, c)prints the function name, line number, argument names, and values tostd::cerr.pr(a, b, c)prints only the values tostd::cerr;pr()prints a blank line.dbg_rows(rows, col_width, limit = -1)prints the function name, line number, argument name, and each iterable row on a separate indexed line. A positivecol_widthright-aligns each element in a field of that width. Pass $0$ for ordinary container formatting. A nonnegativelimitprints at most that many rows and elements per row, marking omitted entries with....pr_rows(rows, col_width, limit = -1)prints only the indexed rows with the same formatting.
Avoid naming these helpers to_string() unless you want them mixed into overload resolution with std::to_string() and other user-defined to_string() functions. A separate name keeps debug formatting local to this snippet.
Implementation
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
template<typename T, typename = void>
struct is_iterable : std::false_type {};
template<typename T>
struct is_iterable<
T, std::void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))>>
: std::true_type {};
template<typename T, typename = void>
struct is_streamable : std::false_type {};
template<typename T>
struct is_streamable<
T, std::void_t<decltype(std::declval<std::ostream &>() << std::declval<const T &>())>>
: std::true_type {};
std::string dbg_repr(const std::string &s) { return '"' + s + '"'; }
std::string dbg_repr(const char *s) { return dbg_repr(std::string(s)); }
std::string dbg_repr(char c) { return std::string("'") + c + "'"; }
std::string dbg_repr(bool b) { return b ? "true" : "false"; }
template<typename T>
typename std::enable_if<
std::is_arithmetic<T>::value && !std::is_same<T, char>::value &&
!std::is_same<T, bool>::value && !std::is_floating_point<T>::value,
std::string>::type
dbg_repr(T x) {
std::ostringstream out;
out << x;
return out.str();
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, std::string>::type dbg_repr(T x) {
std::ostringstream out;
out << std::setprecision(10) << x; // Set floating point dbg precision here.
return out.str();
}
template<typename T>
typename std::enable_if<
is_streamable<T>::value && !is_iterable<T>::value && !std::is_arithmetic<T>::value &&
!std::is_convertible<T, std::string>::value,
std::string>::type
dbg_repr(const T &x) {
std::ostringstream out;
out << x;
return out.str();
}
template<typename A, typename B>
std::string dbg_repr(const std::pair<A, B> &p) {
return "(" + dbg_repr(p.first) + ", " + dbg_repr(p.second) + ")";
}
template<typename Tuple, std::size_t... Is>
std::string dbg_tuple_repr(const Tuple &t, std::index_sequence<Is...>) {
std::string res = "(";
bool first = true;
((res += (first ? (first = false, "") : ", ") + dbg_repr(std::get<Is>(t))), ...);
return res + ")";
}
template<typename... Ts>
std::string dbg_repr(const std::tuple<Ts...> &t) {
return dbg_tuple_repr(t, std::index_sequence_for<Ts...>{});
}
template<typename T>
typename std::enable_if<
is_iterable<T>::value && !std::is_convertible<T, std::string>::value, std::string>::type
dbg_repr(const T &v) {
std::string res = "{";
bool first = true;
for (const auto &x : v) {
if (!first) {
res += ", ";
}
first = false;
res += dbg_repr(x);
}
return res + "}";
}
template<typename Head, typename... Tail>
void dbg_out(bool leading_space, const Head &head, const Tail &...tail) {
if (leading_space) {
std::cerr << ' ';
}
std::cerr << dbg_repr(head);
((std::cerr << ' ' << dbg_repr(tail)), ...);
std::cerr << '\n';
}
void pr_out() {
std::cerr << '\n';
}
template<typename Head, typename... Tail>
void pr_out(const Head &head, const Tail &...tail) {
dbg_out(false, head, tail...);
}
template<typename Rows>
void dbg_rows_out(const Rows &rows, int col_width = 0, int limit = -1) {
int i = 0;
for (const auto &row : rows) {
if (i == limit) {
std::cerr << "...\n";
break;
}
std::cerr << i++ << ':' << (col_width > 0 ? "" : " {");
int j = 0;
for (const auto &x : row) {
if (j == limit) {
std::cerr << (col_width > 0 ? " ..." : (j == 0 ? "..." : ", ..."));
break;
}
std::cerr << (col_width > 0 ? " " : (j == 0 ? "" : ", ")) << std::setw(col_width)
<< dbg_repr(x);
j++;
}
std::cerr << (col_width > 0 ? "\n" : "}\n");
}
}
#ifdef LOCAL
#define dbg(...) \
std::cerr << "[" << __func__ << ":" << __LINE__ << "] " << #__VA_ARGS__ << ":", \
dbg_out(true, __VA_ARGS__)
#define pr(...) pr_out(__VA_ARGS__)
#define dbg_rows(rows, ...) \
std::cerr << "[" << __func__ << ":" << __LINE__ << "] " << #rows << ":\n", \
dbg_rows_out(rows, __VA_ARGS__)
#define pr_rows(rows, ...) dbg_rows_out(rows, __VA_ARGS__)
#else
#define dbg(...) ((void)0)
#define pr(...) ((void)0)
#define dbg_rows(...) ((void)0)
#define pr_rows(...) ((void)0)
#endif
Example Usage
#include <cassert>
#include <map>
#include <vector>
using namespace std;
struct Point {
int x, y;
};
// Can overload dbg_repr to dbg your custom structs.
string dbg_repr(const Point &p) {
return "Point(" + dbg_repr(p.x) + ", " + dbg_repr(p.y) + ")";
}
struct Edge {
int u, v, w;
};
string dbg_repr(const Edge &e) {
return "Edge{" + dbg_repr(e.u) + "->" + dbg_repr(e.v) + ", w=" + dbg_repr(e.w) + "}";
}
struct Streamable {
int value;
friend ostream &operator<<(ostream &out, const Streamable &x) {
return out << "Streamable(" << x.value << ")";
}
};
int main() {
vector<int> v{1, 2, 3};
auto p = make_pair(4, string("x"));
auto t = make_tuple(1, true, string("ok"));
map<string, int> mp{{"a", 1}, {"b", 2}};
Point pt{2, 3};
Edge e{0, 1, 5};
double d = 3.141592653589793;
assert(dbg_repr(v) == "{1, 2, 3}");
assert(dbg_repr(p) == "(4, \"x\")");
assert(dbg_repr(t) == "(1, true, \"ok\")");
assert(dbg_repr(mp) == "{(\"a\", 1), (\"b\", 2)}");
assert(dbg_repr(pt) == "Point(2, 3)");
assert(dbg_repr(e) == "Edge{0->1, w=5}");
assert(dbg_repr(Streamable{6}) == "Streamable(6)");
assert(dbg_repr(vector<Streamable>{{7}, {8}}) == "{Streamable(7), Streamable(8)}");
assert(dbg_repr(1.0 / 3.0) == "0.3333333333");
assert(dbg_repr(d) == "3.141592654");
dbg(v);
dbg(p, t, mp);
dbg(pt, e);
pr("plain output", d);
vector<vector<int>> adj{{1, 2}, {0}, {0}};
pr();
dbg_rows(adj, 0);
vector<vector<int>> grid{{1, 20, -3}, {400, 5, 6}, {7, 8, 9}};
pr();
dbg_rows(grid, 3, 2);
return 0;
}
Example Output
[main:250] v: {1, 2, 3}
[main:251] p, t, mp: (4, "x") (1, true, "ok") {("a", 1), ("b", 2)}
[main:252] pt, e: Point(2, 3) Edge{0->1, w=5}
"plain output" 3.141592654
[main:257] adj:
0: {1, 2}
1: {0}
2: {0}
[main:261] grid:
0: 1 20 ...
1: 400 5 ...
...
/*
Local-only debug printing for contest code. `dbg`, `pr`, `dbg_rows`, and `pr_rows` expand to no-ops
unless `LOCAL` is defined (e.g. via the compiler flag `-DLOCAL`), so they can remain in submitted
code without producing output.
- `dbg_repr(x)` returns a string representation of arithmetic types, strings, pairs, tuples,
iterable containers, and custom types that support insertion into `std::ostream`.
- `dbg(a, b, c)` prints the function name, line number, argument names, and values to `std::cerr`.
- `pr(a, b, c)` prints only the values to `std::cerr`; `pr()` prints a blank line.
- `dbg_rows(rows, col_width, limit = -1)` prints the function name, line number, argument name, and
each iterable row on a separate indexed line. A positive `col_width` right-aligns each element in
a field of that width. Pass $0$ for ordinary container formatting. A nonnegative `limit` prints at
most that many rows and elements per row, marking omitted entries with `...`.
- `pr_rows(rows, col_width, limit = -1)` prints only the indexed rows with the same formatting.
Avoid naming these helpers `to_string()` unless you want them mixed into overload resolution with
`std::to_string()` and other user-defined `to_string()` functions. A separate name keeps debug
formatting local to this snippet.
*/
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
template<typename T, typename = void>
struct is_iterable : std::false_type {};
template<typename T>
struct is_iterable<
T, std::void_t<decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))>>
: std::true_type {};
template<typename T, typename = void>
struct is_streamable : std::false_type {};
template<typename T>
struct is_streamable<
T, std::void_t<decltype(std::declval<std::ostream &>() << std::declval<const T &>())>>
: std::true_type {};
// clang-format off
std::string dbg_repr(const std::string &s) { return '"' + s + '"'; }
std::string dbg_repr(const char *s) { return dbg_repr(std::string(s)); }
std::string dbg_repr(char c) { return std::string("'") + c + "'"; }
std::string dbg_repr(bool b) { return b ? "true" : "false"; }
// clang-format on
template<typename T>
typename std::enable_if<
std::is_arithmetic<T>::value && !std::is_same<T, char>::value &&
!std::is_same<T, bool>::value && !std::is_floating_point<T>::value,
std::string>::type
dbg_repr(T x) {
std::ostringstream out;
out << x;
return out.str();
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, std::string>::type dbg_repr(T x) {
std::ostringstream out;
out << std::setprecision(10) << x; // Set floating point dbg precision here.
return out.str();
}
template<typename T>
typename std::enable_if<
is_streamable<T>::value && !is_iterable<T>::value && !std::is_arithmetic<T>::value &&
!std::is_convertible<T, std::string>::value,
std::string>::type
dbg_repr(const T &x) {
std::ostringstream out;
out << x;
return out.str();
}
template<typename A, typename B>
std::string dbg_repr(const std::pair<A, B> &p) {
return "(" + dbg_repr(p.first) + ", " + dbg_repr(p.second) + ")";
}
template<typename Tuple, std::size_t... Is>
std::string dbg_tuple_repr(const Tuple &t, std::index_sequence<Is...>) {
std::string res = "(";
bool first = true;
((res += (first ? (first = false, "") : ", ") + dbg_repr(std::get<Is>(t))), ...);
return res + ")";
}
template<typename... Ts>
std::string dbg_repr(const std::tuple<Ts...> &t) {
return dbg_tuple_repr(t, std::index_sequence_for<Ts...>{});
}
template<typename T>
typename std::enable_if<
is_iterable<T>::value && !std::is_convertible<T, std::string>::value, std::string>::type
dbg_repr(const T &v) {
std::string res = "{";
bool first = true;
for (const auto &x : v) {
if (!first) {
res += ", ";
}
first = false;
res += dbg_repr(x);
}
return res + "}";
}
template<typename Head, typename... Tail>
void dbg_out(bool leading_space, const Head &head, const Tail &...tail) {
if (leading_space) {
std::cerr << ' ';
}
std::cerr << dbg_repr(head);
((std::cerr << ' ' << dbg_repr(tail)), ...);
std::cerr << '\n';
}
void pr_out() {
std::cerr << '\n';
}
template<typename Head, typename... Tail>
void pr_out(const Head &head, const Tail &...tail) {
dbg_out(false, head, tail...);
}
template<typename Rows>
void dbg_rows_out(const Rows &rows, int col_width = 0, int limit = -1) {
int i = 0;
for (const auto &row : rows) {
if (i == limit) {
std::cerr << "...\n";
break;
}
std::cerr << i++ << ':' << (col_width > 0 ? "" : " {");
int j = 0;
for (const auto &x : row) {
if (j == limit) {
std::cerr << (col_width > 0 ? " ..." : (j == 0 ? "..." : ", ..."));
break;
}
std::cerr << (col_width > 0 ? " " : (j == 0 ? "" : ", ")) << std::setw(col_width)
<< dbg_repr(x);
j++;
}
std::cerr << (col_width > 0 ? "\n" : "}\n");
}
}
#ifdef LOCAL
#define dbg(...) \
std::cerr << "[" << __func__ << ":" << __LINE__ << "] " << #__VA_ARGS__ << ":", \
dbg_out(true, __VA_ARGS__)
#define pr(...) pr_out(__VA_ARGS__)
#define dbg_rows(rows, ...) \
std::cerr << "[" << __func__ << ":" << __LINE__ << "] " << #rows << ":\n", \
dbg_rows_out(rows, __VA_ARGS__)
#define pr_rows(rows, ...) dbg_rows_out(rows, __VA_ARGS__)
#else
#define dbg(...) ((void)0)
#define pr(...) ((void)0)
#define dbg_rows(...) ((void)0)
#define pr_rows(...) ((void)0)
#endif
/*** Example Usage and Output:
[main:250] v: {1, 2, 3}
[main:251] p, t, mp: (4, "x") (1, true, "ok") {("a", 1), ("b", 2)}
[main:252] pt, e: Point(2, 3) Edge{0->1, w=5}
"plain output" 3.141592654
[main:257] adj:
0: {1, 2}
1: {0}
2: {0}
[main:261] grid:
0: 1 20 ...
1: 400 5 ...
...
***/
#include <cassert>
#include <map>
#include <vector>
using namespace std;
struct Point {
int x, y;
};
// Can overload dbg_repr to dbg your custom structs.
string dbg_repr(const Point &p) {
return "Point(" + dbg_repr(p.x) + ", " + dbg_repr(p.y) + ")";
}
struct Edge {
int u, v, w;
};
string dbg_repr(const Edge &e) {
return "Edge{" + dbg_repr(e.u) + "->" + dbg_repr(e.v) + ", w=" + dbg_repr(e.w) + "}";
}
struct Streamable {
int value;
friend ostream &operator<<(ostream &out, const Streamable &x) {
return out << "Streamable(" << x.value << ")";
}
};
int main() {
vector<int> v{1, 2, 3};
auto p = make_pair(4, string("x"));
auto t = make_tuple(1, true, string("ok"));
map<string, int> mp{{"a", 1}, {"b", 2}};
Point pt{2, 3};
Edge e{0, 1, 5};
double d = 3.141592653589793;
assert(dbg_repr(v) == "{1, 2, 3}");
assert(dbg_repr(p) == "(4, \"x\")");
assert(dbg_repr(t) == "(1, true, \"ok\")");
assert(dbg_repr(mp) == "{(\"a\", 1), (\"b\", 2)}");
assert(dbg_repr(pt) == "Point(2, 3)");
assert(dbg_repr(e) == "Edge{0->1, w=5}");
assert(dbg_repr(Streamable{6}) == "Streamable(6)");
assert(dbg_repr(vector<Streamable>{{7}, {8}}) == "{Streamable(7), Streamable(8)}");
assert(dbg_repr(1.0 / 3.0) == "0.3333333333");
assert(dbg_repr(d) == "3.141592654");
dbg(v);
dbg(p, t, mp);
dbg(pt, e);
pr("plain output", d);
vector<vector<int>> adj{{1, 2}, {0}, {0}};
pr();
dbg_rows(adj, 0);
vector<vector<int>> grid{{1, 20, -3}, {400, 5, 6}, {7, 8, 9}};
pr();
dbg_rows(grid, 3, 2);
return 0;
}