A dbg() that prints variable names alongside values, works on any container, and vanishes on the judge.
The macro
#ifdef LOCAL
#define dbg(...) cerr << "[" << #__VA_ARGS__ << "] = ", _print(__VA_ARGS__)
#else
#define dbg(...) 42
#endif#__VA_ARGS__ stringifies the arguments, so dbg(x, y) prints [x, y] = 3 7. Compile locally with -DLOCAL; on the judge the macro expands to a harmless constant and costs nothing.
The printer
void _p(int x) { cerr << x; }
void _p(long long x) { cerr << x; }
void _p(double x) { cerr << x; }
void _p(char x) { cerr << '\'' << x << '\''; }
void _p(const string& x) { cerr << '"' << x << '"'; }
void _p(bool x) { cerr << (x ? "true" : "false"); }
template<class A, class B> void _p(const pair<A,B>& p);
template<class T> void _p(const T& v) { // any iterable
cerr << '{';
bool first = true;
for (const auto& e : v) { if (!first) cerr << ", "; _p(e); first = false; }
cerr << '}';
}
template<class A, class B> void _p(const pair<A,B>& p) {
cerr << '('; _p(p.first); cerr << ", "; _p(p.second); cerr << ')';
}
void _print() { cerr << '\n'; }
template<class T, class... V> void _print(const T& t, const V&... v) {
_p(t);
if (sizeof...(v)) cerr << ", ";
_print(v...);
}The generic _p handles vector, set, map, deque, array, and nested combinations of them β a vector<map<int,pair<int,int>>> prints correctly with no extra code.
Using it
vector<int> a = {3, 1, 4};
map<string,int> m = {{"x", 1}};
dbg(a); // [a] = {3, 1, 4}
dbg(m, a.size()); // [m, a.size()] = {("x", 1)}, 3Why cerr
| Stream | Behaviour |
|---|---|
cout | buffered, mixes into your answer, breaks the judge |
cerr | unbuffered, separate stream, ignored by the judge, redirectable with 2> |
Debug output on cout is a classic self-inflicted WA. Keeping it on cerr means forgetting to remove it costs nothing β though it still costs time on interactive or output-heavy problems, so remove it anyway.
Compiling for debugging
g++ -std=c++20 -O2 -DLOCAL -Wall -Wextra -Wshadow -fsanitize=address,undefined -g main.cpp -o main| Flag | Catches |
|---|---|
-fsanitize=address | out-of-bounds, use-after-free, stack overflow β with the exact line |
-fsanitize=undefined | signed overflow, shifts width, bad casts |
-D_GLIBCXX_DEBUG | out-of-range vector::operator[] and bad iterators |
-Wshadow | a for (int i...) shadowing an outer i |
-Wconversion | silent narrowing (noisy, but occasionally worth a run) |
Sanitizers pay for themselves
Most runtime errors and a good share of wrong answers are out-of-bounds writes. ASan turns a mysterious WA into a stack trace with a line number, usually in under a minute. It is roughly 2x slower β irrelevant when you are debugging a failing case of size 8.
Tracking recursion
#ifdef LOCAL
int depth = 0;
struct Trace {
Trace(const char* n) { cerr << string(depth++ * 2, ' ') << "-> " << n << '\n'; }
~Trace() { depth--; }
};
#define trace(n) Trace _t(n)
#else
#define trace(n)
#endifAn indented call tree is the fastest way to see a recursion that never terminates or that re-explores a state you thought was memoised.
What to reach for instead
For anything longer than a few minutes, a real debugger (gdb, or the IDEβs) beats print statements β a watchpoint on the variable that goes wrong finds the write directly. Print debugging wins on speed of setup, not on power.
See also: Debugging Techniques Β· Stress Testing Β· Contest Template