A C++ debug header that prints anything, and a build that names the line you crashed on
Difference between en2 and en3, changed 16500 character(s)
Hi Codeforces!↵
↵
A mod bug in my combination code recently cost me two wrong submissions on a Div. 3 problem C. Stripped down, it was this:↵
↵
```cpp↵
const int MOD = 1e18 + 7;   // "big enough that I never need a real mod"↵
...↵
fact[i] = fact[i - 1] * i % MOD;↵
```↵
↵
`fact[i - 1] * i` overflows `long long` long before the `% MOD` runs, and the program happily printed garbage. Here is the same code in my current setup, first run, sample input:↵
↵
```↵
✗ Runtime error: UB trap, one of↵
    • signed overflow: a * b or a + b too big for long long↵
    • 1 << j with j >= 31: write 1LL << j↵
    • x / 0 or x % 0↵
    • C-array index out of range↵
    → line 12:  fact[i] = fact[i - 1] * i % MOD;↵
```↵
↵
This post is about the pieces behind that: a debug header that prints anything you give it, and a build that catches the mistakes the judge only reports as "Wrong answer on test 2". Everything is on GitHub: [harly24-cp](https://github.com/Abhayanthk/harly24-cp). It works with any editor; I use Sublime, and the repo includes the build system for it.↵
↵
### Part 1: debug.h↵
↵
The idea is [user:tourist,2026-10-10]'s `debug_out`: a macro that prints names next to values. Mine went through five versions in two years, and each one died in some contest where I needed to print something it couldn't. This is the version that finally prints everything I throw at it:↵
↵
```cpp↵
debug(*min_element(diff.begin() + 2, diff.end()), adj, ms);   // adj: map<string, vector<pair<int, int>>>↵
debug(dp);                                                     // vector<vector<long long>>↵
debug();↵
```↵
↵
```↵
[main:14] *min_element(diff.begin() + 2, diff.end()) = 1 | adj = {"a": [(1, 2)], "b": []} | ms = {2, 2, 5}↵
[main:15] dp =↵
      0: [0, inf, inf, inf]↵
      1: [inf, inf, 5, inf]↵
[main:16] reached↵
```↵
↵
What it handles that the templates I used before (and most I found here) don't:↵
↵
- any nesting, like `map<pair<int, int>, vector<set<int>>>` (overload-based templates only see the overloads declared above them, so nested types often don't compile)↵
- custom comparators and hashes: `set<int, greater<int>>`, an `unordered_map` with your own hash↵
- `multiset` duplicates (some templates print it through a `set`)↵
- arguments with commas, like the `*min_element(...)` call above, which breaks the usual "split the names at commas" macro (the comments of [this blog](https://codeforces.me/blog/entry/76087) ran into it)↵
- `__int128`, C arrays, `stack`, `queue`, `priority_queue` (printed without popping anything) and pb_ds `ordered_set`↵
- DP tables: 2D containers print one row per line with the row index, and your `inf` prints as `inf`↵
- crashes: it writes to `debug.txt` and flushes every line, so a segfault doesn't eat the output, and `debug()` with no arguments prints `reached`, so the last one in the file is the last line that ran↵
- endless loops: a `debug()` inside one used to write gigabytes and freeze my editor; after 10,000 lines it now ends the program↵
↵
Two rules I learned the hard way. First, keep it in a header, never in the submission, and compile locally with `-DLOCAL`:↵
↵
```cpp↵
#ifdef LOCAL↵
#include "debug.h"↵
#else↵
#define debug(...)↵
#endif↵
```↵
↵
Second, don't put `#define int long long` (or any of your template's macros) in the debug header. My old one did, so `int` was 64-bit locally and 32-bit on the judge, which hides exactly the overflow bugs you want to find.↵
↵
How it works: a single `print` template that dispatches with `if constexpr` on C++20 concepts (string-like, range, map, container adaptor, tuple-like, streamable). It calls itself for nested types, so there is no declaration order to get wrong. Names are split at top-level commas only, with brackets and string literals tracked.↵
↵
<spoiler summary="debug.h (215 lines, C++20)">↵
```cpp↵
// debug.h: local-only debug printer. Included through AllHeaders.h when built with -DLOCAL.↵
//↵
//   debug(x, v, mp);     [Harly24:42] x = 5 | v = [1, 2, 3] | mp = {1: [2, 3]}↵
//   debug();             [Harly24:42] reached        (crash hunting: the last line printed is the last line reached)↵
//   debug("after dfs");  [Harly24:42] after dfs      (a string literal prints as a label)↵
//   debug(dp);           grids (vector<vector<..>>, vector<string>, adjacency lists) print one row per line↵
//↵
// Prints any nesting of numbers, __int128, char, bool, strings, pair, tuple, every STL container↵
// (custom comparators and hashes too), C arrays, stack/queue/priority_queue, pb_ds ordered_set,↵
// and any type with operator<<. Writes to debug.txt and flushes every line, so output survives a crash.↵
// If debug() floods (over 10000 lines or 4 MB: an endless loop), the run ends with exit code 86.↵
//↵
// Self-contained on purpose: it is included before the template's macros (#define int long long, ...)↵
// so local types match the judge's.↵
↵
#pragma once↵
#include <bits/stdc++.h>↵
↵
namespace dbg {↵
↵
// ---- settings ----↵
inline constexpr std::size_t max_items = 100;     // per container; the rest prints as "...+N more"↵
inline constexpr std::size_t max_lines = 10000;   // per run; keeps debug.txt small enough for Sublime↵
inline constexpr std::streamoff max_bytes = 4 << 20;   // 4 MB, for runs with few but huge lines↵
inline constexpr bool stop_on_flood = true;       // past either cap, end the program (exit code 86): it's a runaway loop↵
inline constexpr long long shown_as_inf = 2e18;   // the template's inf, printed as "inf"↵
↵
inline std::ofstream file = [] { std::ofstream f("debug.txt"); f.precision(10); return f; }();↵
inline std::ostream& out() { return file.is_open() ? static_cast<std::ostream&>(file) : std::cerr; }↵
inline std::size_t lines_written = 0;↵
inline bool stopped = false;↵
↵
inline bool flooded() {↵
    if (stopped) return true;↵
    if (lines_written < max_lines && !(file.is_open() && file.tellp() > max_bytes)) return false;↵
    out() << "[debug] flood: over " << max_lines << " lines or " << (max_bytes >> 20)↵
          << " MB. Endless loop? (caps are in debug.h)" << std::endl;↵
    stopped = true;↵
    if (stop_on_flood) { std::cout.flush(); std::_Exit(86); }↵
    return true;↵
}↵
↵
// ---- type tests ----↵
template <class T> using bare = std::remove_cvref_t<T>;↵
template <class> inline constexpr bool always_false = false;↵
↵
template <class T> concept StringLike = std::is_convertible_v<const T&, std::string_view>;↵
template <class T> concept Range = requires(const T& x) { std::begin(x); std::end(x); };↵
template <class T> concept Adaptor = requires { typename T::container_type; } && !Range<T>;↵
template <class T> concept TupleLike = requires { std::tuple_size<T>::value; } && !Range<T>;↵
template <class T> concept Streamable = requires(std::ostream& o, const T& x) { o << x; };↵
template <class T> concept SetOrMap = requires { typename T::key_type; };↵
template <class T> concept MapLike = requires { typename T::mapped_type; };↵
template <class T> concept IsPair = requires(const T& p) { p.first; p.second; };↵
template <class T> using element_t = bare<decltype(*std::begin(std::declval<const T&>()))>;↵
template <class T> concept Grid = Range<T> && !StringLike<T> && Range<element_t<T>>;↵
↵
// stack/queue/priority_queue keep their container in a protected member `c`.↵
template <class A>↵
const typename A::container_type& underlying(const A& a) {↵
    struct Peek : A {↵
        static const typename A::container_type& get(const A& x) { return x.*&Peek::c; }↵
    };↵
    return Peek::get(a);↵
}↵
↵
inline std::string i128_to_string(unsigned __int128 x, bool negative) {↵
    std::string s;↵
    do { s += char('0' + int(x % 10)); x /= 10; } while (x);↵
    if (negative) s += '-';↵
    return {s.rbegin(), s.rend()};↵
}↵
↵
// ---- printer ----↵
template <class T> void print(std::ostream& o, const T& x);↵
↵
template <class R> void print_items(std::ostream& o, const R& r, const char* open, const char* close) {↵
    o << open;↵
    std::size_t shown = 0, total = 0;↵
    for (const auto& item : r) {↵
        if (shown < max_items) {↵
            if (shown++) o << ", ";↵
            if constexpr (MapLike<R> && IsPair<bare<decltype(item)>>) {↵
                print(o, item.first); o << ": "; print(o, item.second);↵
            } else {↵
                print(o, item);↵
            }↵
        }↵
        ++total;↵
    }↵
    if (total > shown) o << ", ...+" << total - shown << " more";↵
    o << close;↵
}↵
↵
template <class T> void print(std::ostream& o, const T& x) {↵
    if constexpr (std::is_same_v<T, char>) {↵
        o << '\'' << x << '\'';↵
    } else if constexpr (std::is_same_v<T, bool>) {↵
        o << (x ? "true" : "false");↵
    } else if constexpr (StringLike<T>) {↵
        if constexpr (std::is_pointer_v<T>) { if (!x) { o << "nullptr"; return; } }↵
        o << '"' << std::string_view(x) << '"';↵
    } else if constexpr (std::is_same_v<T, __int128>) {↵
        o << i128_to_string(x < 0 ? -(unsigned __int128)x : (unsigned __int128)x, x < 0);↵
    } else if constexpr (std::is_same_v<T, unsigned __int128>) {↵
        o << i128_to_string(x, false);↵
    } else if constexpr (std::is_integral_v<T>) {↵
        if constexpr (std::is_signed_v<T>) {↵
            if (x == std::numeric_limits<T>::max() || (long long)x == shown_as_inf) { o << "inf"; return; }↵
            if (x == std::numeric_limits<T>::min() || (long long)x == -shown_as_inf) { o << "-inf"; return; }↵
        }↵
        o << +x;↵
    } else if constexpr (std::is_floating_point_v<T>) {↵
        o << x;↵
    } else if constexpr (Range<T>) {↵
        if constexpr (SetOrMap<T>) print_items(o, x, "{", "}");↵
        else print_items(o, x, "[", "]");↵
    } else if constexpr (Adaptor<T>) {↵
        if constexpr (requires { typename T::value_compare; }) {↵
            T copy = x;                                   // priority_queue: pop order, top first↵
            std::vector<typename T::value_type> order;↵
            while (!copy.empty()) { order.push_back(copy.top()); copy.pop(); }↵
            print_items(o, order, "[", "]");↵
        } else {↵
            print_items(o, underlying(x), "[", "]");      // stack: bottom..top, queue: front..back↵
        }↵
    } else if constexpr (TupleLike<T>) {↵
        o << '(';↵
        std::apply([&](const auto&... e) { std::size_t i = 0; ((o << (i++ ? ", " : ""), print(o, e)), ...); }, x);↵
        o << ')';↵
    } else if constexpr (Streamable<T>) {↵
        o << x;↵
    } else {↵
        static_assert(always_false<T>, "debug(): no printer for this type. Give it `friend ostream& operator<<(ostream&, const T&)` or debug its fields.");↵
    }↵
}↵
↵
template <class T> void print_value(std::ostream& o, const T& x) {↵
    if constexpr (Grid<T>) {↵
        std::size_t rows = 0;↵
        for (const auto& r : x) {↵
            if (rows < max_items) {↵
                o << "\n    " << std::setw(3) << rows << ": ";↵
                print(o, r);↵
                ++lines_written;↵
            }↵
            ++rows;↵
        }↵
        if (rows == 0) o << "[]";↵
        if (rows > max_items) o << "\n    ...+" << rows - max_items << " more rows";↵
    } else {↵
        print(o, x);↵
    }↵
}↵
↵
// "a, f(b, c), mp[{1, 2}]" -> {"a", "f(b, c)", "mp[{1, 2}]"}: splits at top-level commas only.↵
inline std::vector<std::string> split_names(std::string_view s) {↵
    std::vector<std::string> names(1);↵
    int depth = 0;↵
    char quote = 0;↵
    for (std::size_t i = 0; i < s.size(); ++i) {↵
        char c = s[i];↵
        if (quote) {↵
            if (c == '\\' && i + 1 < s.size()) { names.back() += c; c = s[++i]; }↵
            else if (c == quote) quote = 0;↵
        } else if (c == '"' || c == '\'') {↵
            quote = c;↵
        } else if (c == '(' || c == '[' || c == '{') {↵
            ++depth;↵
        } else if (c == ')' || c == ']' || c == '}') {↵
            --depth;↵
        } else if (c == ',' && depth == 0) {↵
            names.emplace_back();↵
            continue;↵
        }↵
        names.back() += c;↵
    }↵
    for (auto& n : names) {↵
        auto first = n.find_first_not_of(' '), last = n.find_last_not_of(' ');↵
        n = first == std::string::npos ? "" : n.substr(first, last - first + 1);↵
    }↵
    return names;↵
}↵
↵
template <class... Ts>↵
void emit(const char* func, int line, const char* names, const Ts&... values) {↵
    if (flooded()) return;↵
    std::ostream& o = out();↵
    o << '[' << func << ':' << line << "] ";↵
    if constexpr (sizeof...(Ts) == 0) {↵
        o << "reached";↵
    } else {↵
        const std::vector<std::string> parts = split_names(names);↵
        const bool named = parts.size() == sizeof...(Ts);   // false only when a comma sits inside <...>↵
        if (!named) o << names << " = ";↵
        std::size_t i = 0;↵
        auto one = [&](const auto& v) {↵
            if (i) o << " | ";↵
            if constexpr (StringLike<bare<decltype(v)>>) {↵
                if (named && parts[i].starts_with('"')) { o << std::string_view(v); ++i; return; }↵
            }↵
            if (named) o << parts[i] << (Grid<bare<decltype(v)>> ? " =" : " = ");↵
            print_value(o, v);↵
            ++i;↵
        };↵
        (one(values), ...);↵
    }↵
    o << '\n';↵
    o.flush();↵
    ++lines_written;↵
}↵
↵
}  // namespace dbg↵
↵
#define debug(...) ::dbg::emit(__func__, __LINE__, #__VA_ARGS__ __VA_OPT__(,) __VA_ARGS__)↵
```↵
</spoiler>↵
↵
Your own struct only needs an `operator<<`:↵
↵
```cpp↵
struct Edge { int u, v, w; friend ostream& operator<<(ostream& o, const Edge& e) { return o << e.u << "-" << e.v << " (" << e.w << ")"; } };↵
```↵
↵
### Part 2: the build↵
↵
[user:andreyv,2026-10-10]'s [Catching silly mistakes with GCC](https://codeforces.me/blog/entry/15547) is still the best list of debug flags. This is the set I run on every Ctrl+B; it's fast enough to never turn off:↵
↵
```↵
g++ -std=c++20 -O1 -g1 -DLOCAL -D_GLIBCXX_DEBUG \↵
    -fsanitize=signed-integer-overflow,shift,integer-divide-by-zero,bounds -fsanitize-trap=all \↵
    -Wall -Wextra -Wshadow=local -Werror=return-type↵
```↵
↵
`-fsanitize-trap=all` (on GCC 13 and older: `-fsanitize-undefined-trap-on-error`) needs no runtime library, so it also works with Homebrew GCC on macOS, where `-fsanitize=undefined` doesn't even link. It stops at the faulting instruction without a message, so my script reruns the program once under lldb or gdb and prints the line, as in the example at the top.↵
↵
A few things I only found out while building this:↵
↵
- `x / 0` doesn't crash on Apple Silicon: ARM's divide instruction returns 0, so the code passes locally and gets a runtime error on the judge. The trap above catches it.↵
- The default stack on macOS is 8 MB and Codeforces gives 256 MB, so a DFS that goes 10^6 deep segfaults locally and passes on the judge. On macOS link with `-Wl,-stack_size,0x20000000`; on Linux use `ulimit -s`.↵
- On macOS, `g++` is Apple clang: no pb_ds, and not what the judge runs. Use Homebrew's `g++-15`.↵
- GCC 16's `<bits/stdc++.h>` no longer declares `assert`. If you use `assert`, add `#include <cassert>` before your compiler, or the judge's, upgrades.↵
- `-D_GLIBCXX_DEBUG` catches a sort comparator that uses `<=` ("comparison doesn't meet irreflexive requirements"), a classic runtime error on the judge.↵
↵
The script (`cprun`) wraps all of this with three guards I added after an endless loop froze my Mac: a time limit, a memory watchdog (macOS ignores `ulimit -v`, and `push_back` in an endless loop can swap the whole machine), and a 10 MB cap on output files. Every run ends with one verdict and the CPU time, which is what the judge measures.↵
↵
### Part 3: a generator for max tests↵
↵
Samples never tell you about TLE. `gen.h` writes the common Codeforces input formats in one call, respecting "sum of n over all tests":↵
↵
```cpp↵
arrays(10, 200000, 1, 1e9);                  // t = 10 tests, sum of n = 2e5↵
trees(1, 200000, "line");                    // one long path, for deep DFS↵
update_queries(0, 200000, 200000, 1, 1e9);   // no t line: n q / array / queries↵
```↵
↵
There are 20 formats, plus the building blocks for anything else.↵
↵
### That's it↵
↵
Repo: [github.com/Abhayanthk/harly24-cp](https://github.com/Abhayanthk/harly24-cp). It runs on macOS and Linux (WSL on Windows), with tests on both in CI. If `debug()` can't print some type you use, post it in the comments and I'll add it.↵
↵
Thanks to [user:tourist,2026-10-10] for the `debug_out` idea, and to [user:andreyv,2026-10-10] for the flags blog that started all of this for me.↵
Hi Codeforces!↵
↵
A mod bug in my combination code recently cost me two wrong submissions on a Div. 3 problem C. Stripped down, it was this:↵
↵
```cpp↵
const int MOD = 1e18 + 7;   // "big enough that I never need a real mod"↵
...↵
fact[i] = fact[i - 1] * i % MOD;↵
```↵
↵
`fact[i - 1] * i` overflows `long long` long before the `% MOD` runs, and the program happily printed garbage. Here is the same code in my current setup, first run, sample input:↵
↵
```↵
✗ Runtime error: UB trap, one of↵
    • signed overflow: a * b or a + b too big for long long↵
    • 1 << j with j >= 31: write 1LL << j↵
    • x / 0 or x % 0↵
    • C-array index out of range↵
    → line 12:  fact[i] = fact[i - 1] * i % MOD;↵
```↵
↵
This post is about the pieces behind that: a debug header that prints anything you give it, and a build that catches the mistakes the judge only reports as "Wrong answer on test 2". Everything is on GitHub: [harly24-cp](https://github.com/Abhayanthk/harly24-cp). It works with any editor; I use Sublime, and the repo includes the build system for it.↵
↵
### Part 1: debug.h↵
↵
The idea is [user:tourist,2026-10-10]'s `debug_out`: a macro that prints names next to values. Mine went through five versions in two years, and each one died in some contest where I needed to print something it couldn't. This is the version that finally prints everything I throw at it:↵
↵
```cpp↵
debug(*min_element(diff.begin() + 2, diff.end()), adj, ms);   // adj: map<string, vector<pair<int, int>>>↵
debug(dp);                                                     // vector<vector<long long>>↵
debug();↵
```↵
↵
```↵
[main:14] *min_element(diff.begin() + 2, diff.end()) = 1 | adj = {"a": [(1, 2)], "b": []} | ms = {2, 2, 5}↵
[main:15] dp =↵
      0: [0, inf, inf, inf]↵
      1: [inf, inf, 5, inf]↵
[main:16] reached↵
```↵
↵
What it handles that the templates I used before (and most I found here) don't:↵
↵
- any nesting, like `map<pair<int, int>, vector<set<int>>>` (overload-based templates only see the overloads declared above them, so nested types often don't compile)↵
- custom comparators and hashes: `set<int, greater<int>>`, an `unordered_map` with your own hash↵
- `multiset` duplicates (some templates print it through a `set`)↵
- arguments with commas, like the `*min_element(...)` call above, which breaks the usual "split the names at commas" macro (the comments of [this blog](https://codeforces.me/blog/entry/76087) ran into it)↵
- `__int128`, C arrays, `stack`, `queue`, `priority_queue` (printed without popping anything) and pb_ds `ordered_set`↵
- DP tables: 2D containers print one row per line with the row index, and your `inf` prints as `inf`↵
- crashes: it writes to `debug.txt` and flushes every line, so a segfault doesn't eat the output, and `debug()` with no arguments prints `reached`, so the last one in the file is the last line that ran↵
- endless loops: a `debug()` inside one used to write gigabytes and freeze my editor; after 10,000 lines it now ends the program↵
↵
Two rules I learned the hard way. First, keep it in a header, never in the submission, and compile locally with `-DLOCAL`:↵
↵
```cpp↵
#ifdef LOCAL↵
#include "debug.h"↵
#else↵
#define debug(...)↵
#endif↵
```↵
↵
Second, don't put `#define int long long` (or any of your template's macros) in the debug header. My old one did, so `int` was 64-bit locally and 32-bit on the judge, which hides exactly the overflow bugs you want to find.↵
↵
How it works: a single `print` template that dispatches with `if constexpr` on C++20 concepts (string-like, range, map, container adaptor, tuple-like, streamable). It calls itself for nested types, so there is no declaration order to get wrong. Names are split at top-level commas only, with brackets and string literals tracked.↵
↵
<spoiler summary="debug.h (215 lines, C++20)">↵
```cpp↵
// debug.h: local-only debug printer. Included through AllHeaders.h when built with -DLOCAL.↵
//↵
//   debug(x, v, mp);     [Harly24:42] x = 5 | v = [1, 2, 3] | mp = {1: [2, 3]}↵
//   debug();             [Harly24:42] reached        (crash hunting: the last line printed is the last line reached)↵
//   debug("after dfs");  [Harly24:42] after dfs      (a string literal prints as a label)↵
//   debug(dp);           grids (vector<vector<..>>, vector<string>, adjacency lists) print one row per line↵
//↵
// Prints any nesting of numbers, __int128, char, bool, strings, pair, tuple, every STL container↵
// (custom comparators and hashes too), C arrays, stack/queue/priority_queue, pb_ds ordered_set,↵
// and any type with operator<<. Writes to debug.txt and flushes every line, so output survives a crash.↵
// If debug() floods (over 10000 lines or 4 MB: an endless loop), the run ends with exit code 86.↵
//↵
// Self-contained on purpose: it is included before the template's macros (#define int long long, ...)↵
// so local types match the judge's.↵
↵
#pragma once↵
#include <bits/stdc++.h>↵
↵
namespace dbg {↵
↵
// ---- settings ----↵
inline constexpr std::size_t max_items = 100;     // per container; the rest prints as "...+N more"↵
inline constexpr std::size_t max_lines = 10000;   // per run; keeps debug.txt small enough for Sublime↵
inline constexpr std::streamoff max_bytes = 4 << 20;   // 4 MB, for runs with few but huge lines↵
inline constexpr bool stop_on_flood = true;       // past either cap, end the program (exit code 86): it's a runaway loop↵
inline constexpr long long shown_as_inf = 2e18;   // the template's inf, printed as "inf"↵
↵
inline std::ofstream file = [] { std::ofstream f("debug.txt"); f.precision(10); return f; }();↵
inline std::ostream& out() { return file.is_open() ? static_cast<std::ostream&>(file) : std::cerr; }↵
inline std::size_t lines_written = 0;↵
inline bool stopped = false;↵
↵
inline bool flooded() {↵
    if (stopped) return true;↵
    if (lines_written < max_lines && !(file.is_open() && file.tellp() > max_bytes)) return false;↵
    out() << "[debug] flood: over " << max_lines << " lines or " << (max_bytes >> 20)↵
          << " MB. Endless loop? (caps are in debug.h)" << std::endl;↵
    stopped = true;↵
    if (stop_on_flood) { std::cout.flush(); std::_Exit(86); }↵
    return true;↵
}↵
↵
// ---- type tests ----↵
template <class T> using bare = std::remove_cvref_t<T>;↵
template <class> inline constexpr bool always_false = false;↵
↵
template <class T> concept StringLike = std::is_convertible_v<const T&, std::string_view>;↵
template <class T> concept Range = requires(const T& x) { std::begin(x); std::end(x); };↵
template <class T> concept Adaptor = requires { typename T::container_type; } && !Range<T>;↵
template <class T> concept TupleLike = requires { std::tuple_size<T>::value; } && !Range<T>;↵
template <class T> concept Streamable = requires(std::ostream& o, const T& x) { o << x; };↵
template <class T> concept SetOrMap = requires { typename T::key_type; };↵
template <class T> concept MapLike = requires { typename T::mapped_type; };↵
template <class T> concept IsPair = requires(const T& p) { p.first; p.second; };↵
template <class T> using element_t = bare<decltype(*std::begin(std::declval<const T&>()))>;↵
template <class T> concept Grid = Range<T> && !StringLike<T> && Range<element_t<T>>;↵
↵
// stack/queue/priority_queue keep their container in a protected member `c`.↵
template <class A>↵
const typename A::container_type& underlying(const A& a) {↵
    struct Peek : A {↵
        static const typename A::container_type& get(const A& x) { return x.*&Peek::c; }↵
    };↵
    return Peek::get(a);↵
}↵
↵
inline std::string i128_to_string(unsigned __int128 x, bool negative) {↵
    std::string s;↵
    do { s += char('0' + int(x % 10)); x /= 10; } while (x);↵
    if (negative) s += '-';↵
    return {s.rbegin(), s.rend()};↵
}↵
↵
// ---- printer ----↵
template <class T> void print(std::ostream& o, const T& x);↵
↵
template <class R> void print_items(std::ostream& o, const R& r, const char* open, const char* close) {↵
    o << open;↵
    std::size_t shown = 0, total = 0;↵
    for (const auto& item : r) {↵
        if (shown < max_items) {↵
            if (shown++) o << ", ";↵
            if constexpr (MapLike<R> && IsPair<bare<decltype(item)>>) {↵
                print(o, item.first); o << ": "; print(o, item.second);↵
            } else {↵
                print(o, item);↵
            }↵
        }↵
        ++total;↵
    }↵
    if (total > shown) o << ", ...+" << total - shown << " more";↵
    o << close;↵
}↵
↵
template <class T> void print(std::ostream& o, const T& x) {↵
    if constexpr (std::is_same_v<T, char>) {↵
        o << '\'' << x << '\'';↵
    } else if constexpr (std::is_same_v<T, bool>) {↵
        o << (x ? "true" : "false");↵
    } else if constexpr (StringLike<T>) {↵
        if constexpr (std::is_pointer_v<T>) { if (!x) { o << "nullptr"; return; } }↵
        o << '"' << std::string_view(x) << '"';↵
    } else if constexpr (std::is_same_v<T, __int128>) {↵
        o << i128_to_string(x < 0 ? -(unsigned __int128)x : (unsigned __int128)x, x < 0);↵
    } else if constexpr (std::is_same_v<T, unsigned __int128>) {↵
        o << i128_to_string(x, false);↵
    } else if constexpr (std::is_integral_v<T>) {↵
        if constexpr (std::is_signed_v<T>) {↵
            if (x == std::numeric_limits<T>::max() || (long long)x == shown_as_inf) { o << "inf"; return; }↵
            if (x == std::numeric_limits<T>::min() || (long long)x == -shown_as_inf) { o << "-inf"; return; }↵
        }↵
        o << +x;↵
    } else if constexpr (std::is_floating_point_v<T>) {↵
        o << x;↵
    } else if constexpr (Range<T>) {↵
        if constexpr (SetOrMap<T>) print_items(o, x, "{", "}");↵
        else print_items(o, x, "[", "]");↵
    } else if constexpr (Adaptor<T>) {↵
        if constexpr (requires { typename T::value_compare; }) {↵
            T copy = x;                                   // priority_queue: pop order, top first↵
            std::vector<typename T::value_type> order;↵
            while (!copy.empty()) { order.push_back(copy.top()); copy.pop(); }↵
            print_items(o, order, "[", "]");↵
        } else {↵
            print_items(o, underlying(x), "[", "]");      // stack: bottom..top, queue: front..back↵
        }↵
    } else if constexpr (TupleLike<T>) {↵
        o << '(';↵
        std::apply([&](const auto&... e) { std::size_t i = 0; ((o << (i++ ? ", " : ""), print(o, e)), ...); }, x);↵
        o << ')';↵
    } else if constexpr (Streamable<T>) {↵
        o << x;↵
    } else {↵
        static_assert(always_false<T>, "debug(): no printer for this type. Give it `friend ostream& operator<<(ostream&, const T&)` or debug its fields.");↵
    }↵
}↵
↵
template <class T> void print_value(std::ostream& o, const T& x) {↵
    if constexpr (Grid<T>) {↵
        std::size_t rows = 0;↵
        for (const auto& r : x) {↵
            if (rows < max_items) {↵
                o << "\n    " << std::setw(3) << rows << ": ";↵
                print(o, r);↵
                ++lines_written;↵
            }↵
            ++rows;↵
        }↵
        if (rows == 0) o << "[]";↵
        if (rows > max_items) o << "\n    ...+" << rows - max_items << " more rows";↵
    } else {↵
        print(o, x);↵
    }↵
}↵
↵
// "a, f(b, c), mp[{1, 2}]" -> {"a", "f(b, c)", "mp[{1, 2}]"}: splits at top-level commas only.↵
inline std::vector<std::string> split_names(std::string_view s) {↵
    std::vector<std::string> names(1);↵
    int depth = 0;↵
    char quote = 0;↵
    for (std::size_t i = 0; i < s.size(); ++i) {↵
        char c = s[i];↵
        if (quote) {↵
            if (c == '\\' && i + 1 < s.size()) { names.back() += c; c = s[++i]; }↵
            else if (c == quote) quote = 0;↵
        } else if (c == '"' || c == '\'') {↵
            quote = c;↵
        } else if (c == '(' || c == '[' || c == '{') {↵
            ++depth;↵
        } else if (c == ')' || c == ']' || c == '}') {↵
            --depth;↵
        } else if (c == ',' && depth == 0) {↵
            names.emplace_back();↵
            continue;↵
        }↵
        names.back() += c;↵
    }↵
    for (auto& n : names) {↵
        auto first = n.find_first_not_of(' '), last = n.find_last_not_of(' ');↵
        n = first == std::string::npos ? "" : n.substr(first, last - first + 1);↵
    }↵
    return names;↵
}↵
↵
template <class... Ts>↵
void emit(const char* func, int line, const char* names, const Ts&... values) {↵
    if (flooded()) return;↵
    std::ostream& o = out();↵
    o << '[' << func << ':' << line << "] ";↵
    if constexpr (sizeof...(Ts) == 0) {↵
        o << "reached";↵
    } else {↵
        const std::vector<std::string> parts = split_names(names);↵
        const bool named = parts.size() == sizeof...(Ts);   // false only when a comma sits inside <...>↵
        if (!named) o << names << " = ";↵
        std::size_t i = 0;↵
        auto one = [&](const auto& v) {↵
            if (i) o << " | ";↵
            if constexpr (StringLike<bare<decltype(v)>>) {↵
                if (named && parts[i].starts_with('"')) { o << std::string_view(v); ++i; return; }↵
            }↵
            if (named) o << parts[i] << (Grid<bare<decltype(v)>> ? " =" : " = ");↵
            print_value(o, v);↵
            ++i;↵
        };↵
        (one(values), ...);↵
    }↵
    o << '\n';↵
    o.flush();↵
    ++lines_written;↵
}↵
↵
}  // namespace dbg↵
↵
#define debug(...) ::dbg::emit(__func__, __LINE__, #__VA_ARGS__ __VA_OPT__(,) __VA_ARGS__)↵
```↵
</spoiler>↵
↵
Your own struct only needs an `operator<<`:↵
↵
```cpp↵
struct Edge { int u, v, w; friend ostream& operator<<(ostream& o, const Edge& e) { return o << e.u << "-" << e.v << " (" << e.w << ")"; } };↵
```↵
↵
### Part 2: the build↵
↵
[user:andreyv,2026-10-10]'s [Catching silly mistakes with GCC](https://codeforces.me/blog/entry/15547) is still the best list of debug flags. This is the set I run on every Ctrl+B; it's fast enough to never turn off:↵
↵
```↵
g++ -std=c++20 -O1 -g1 -DLOCAL -D_GLIBCXX_DEBUG \↵
    -fsanitize=signed-integer-overflow,shift,integer-divide-by-zero,bounds -fsanitize-trap=all \↵
    -Wall -Wextra -Wshadow=local -Werror=return-type↵
```↵
↵
`-fsanitize-trap=all` (on GCC 13 and older: `-fsanitize-undefined-trap-on-error`) needs no runtime library, so it also works with Homebrew GCC on macOS, where `-fsanitize=undefined` doesn't even link. It stops at the faulting instruction without a message, so my script reruns the program once under lldb or gdb and prints the line, as in the example at the top.↵
↵
A few things I only found out while building this:↵
↵
- `x / 0` doesn't crash on Apple Silicon: ARM's divide instruction returns 0, so the code passes locally and gets a runtime error on the judge. The trap above catches it.↵
- The default stack on macOS is 8 MB and Codeforces gives 256 MB, so a DFS that goes 10^6 deep segfaults locally and passes on the judge. On macOS link with `-Wl,-stack_size,0x20000000`; on Linux use `ulimit -s`.↵
- On macOS, `g++` is Apple clang: no pb_ds, and not what the judge runs. Use Homebrew's `g++-15`.↵
- GCC 16's `<bits/stdc++.h>` no longer declares `assert`. If you use `assert`, add `#include <cassert>` before your compiler, or the judge's, upgrades.↵
- `-D_GLIBCXX_DEBUG` catches a sort comparator that uses `<=` ("comparison doesn't meet irreflexive requirements"), a classic runtime error on the judge.↵
↵
The script (`cprun`) wraps all of this with three guards I added after an endless loop froze my Mac: a time limit, a memory watchdog (macOS ignores `ulimit -v`, and `push_back` in an endless loop can swap the whole machine), and a 10 MB cap on output files. Every run ends with one verdict and the CPU time, which is what the judge measures.↵
↵
### Part 3: a generator for max tests↵
↵
Samples never tell you about TLE. `gen.h` writes the common Codeforces input formats in one call, respecting "sum of n over all tests":↵
↵
```cpp↵
arrays(10, 200000, 1, 1e9);                  // t = 10 tests, sum of n = 2e5↵
trees(1, 200000, "line");                    // one long path, for deep DFS↵
update_queries(0, 200000, 200000, 1, 1e9);   // no t line: n q / array / queries↵
```↵
↵
There are 20 formats, plus the building blocks for anything else.↵
↵
### That's it↵
↵
Repo: [github.com/Abhayanthk/harly24-cp](https://github.com/Abhayanthk/harly24-cp). It runs on macOS and Linux (WSL on Windows), with tests on both in CI. If `debug()` can't print some type you use, post it in the comments and I'll add it.↵
↵
Thanks to [user:tourist,2026-10-10] for the `debug_out` idea, and to [user:andreyv,2026-10-10] for the flags blog that started all of this for me.↵

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en3 English Harly24 2026-10-10 19:07:01 16500
en2 English Harly24 2026-10-10 19:04:23 20906
en1 English Harly24 2026-10-10 16:00:30 16647 Initial revision (published)