算法竞赛自用 debug 库

算法竞赛自用 debug 库

Coast23

打 Codeforces contest、刷 OJ 的时候经常需要调试代码,但我既不喜欢打断点,又觉得手动写 printf 实在是麻烦,于是便决定搞一个 debug 库来偷懒。

现成的 debug 方案已有很多,比如上 CF 薅别人的 debug 宏,或者用dbg-macro,但前者无法打印 STL 容器,后者有太多不必要的功能和输出内容,所以我就调教 AI 写了一个简洁的 debug 库,支持输出以下内容:

  • “基础”类型,如 int, double, char, __int128, bitset, pair, tuple 等;
  • C 风格数组,如 int a[10]bool s[5] 等;
  • 字符串,包括 std::stringchar*
  • STL 容器,要求 iterable 且元素可打印,如 vector, map, set, array, deque 等。

其它功能特性:

  • 可一次打印多个变量,如 debug(a, b, c)
  • 递归解析,嵌套类型会全部展开,如 vector<vector<int>>
  • 因为是结合 CPH 使用,而不是在终端输出,所以没做 ANSI 颜色输出;
  • 不支持非字符串指针;
  • 不支持自定义的类、结构体等;
  • 不支持 queue, stack, priority_queue 等 non-iterable 容器(可以拷贝后再打印,但我觉得不需要就没做)。

使用示例:

#include <bits/stdc++.h>
#define ll long long

#ifdef LOCAL // 本地编译时添加 -D LOCAL 编译选项
#include "debug.h"
#else
#define debug(...) (__VA_ARGS__)
#define dbg(...) (__VA_ARGS__)
#endif

signed main(){
// debug() 会输出函数名和行号,而 dbg() 不会。
int a = 114; double b = 3.14; char c = 'c'; bool d = false;
debug(a, b, c, d);

std::pair<int, float> p = {1, 2.0};
std::tuple<int, std::string, __int128> t = {-514, "hello", -1e22};
debug(p, t);

std::vector<std::vector<int>> adj(5);
for(int i = 1; i < 5; ++i){
adj[i].push_back(i - 1);
}
std::deque<char> dq = {'a', 'b', 'c', 'd'};
dbg(adj, dq);

std::multiset<int> s = {1, 1, 4, 5, 1, 4};
dbg(s);

std::unordered_map<std::string, int> dict;
dict["apple"] = 5, dict["banana"] = 6, dict["orange"] = 6;
dbg(dict);

std::bitset<8> bits(0b10101010);
debug(bits);
}

输出:

main:14  a = 114, b = 3.14, c = 'c', d = false
main:18 p = (1, 2), t = (-514, "hello", -10000000000000000000000)
adj = {{}, {0}, {1}, {2}, {3}}, dq = {'a', 'b', 'c', 'd'}
s = {1, 1, 1, 4, 4, 5}
dict = {("orange", 6), ("banana", 6), ("apple", 5)}
main:35 bits = 10101010

debug.h 内容如下,需要自取:

#pragma once
#include <bitset>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
#include <type_traits>

namespace dbg {

using std::cerr;
using std::string;

// ==================
// 类型检测
// ==================

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>
constexpr bool is_iterable_v = is_iterable<T>::value;

// ==================
// 基础类型输出
// ==================

// arithmetic
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value>::type
__print(const T& x){
cerr << std::boolalpha << x;
}

// __int128
inline void __print(__int128 x){
if(!x) return cerr << 0, void();
if(x < 0) cerr << '-', x = -x;

std::string s;
while(x){
s.push_back((x % 10) ^ 48);
x /= 10;
}
std::reverse(s.begin(), s.end());
cerr << s;
}

inline void __print(unsigned __int128 x){
if(!x) return cerr << 0, void();

std::string s;
while(x){
s.push_back((x % 10) ^ 48);
x /= 10;
}
std::reverse(s.begin(), s.end());
cerr << s;
}

// string
inline void __print(const string& x){
cerr << '"' << x << '"';
}

// C-string
inline void __print(const char* x){
cerr << '"' << x << '"';
}

// char
inline void __print(char x){
cerr << '\'' << x << '\'';
}

// bitset
template <size_t N>
void __print(const std::bitset<N>& b){
cerr << b;
}

// pair
template <typename A, typename B>
void __print(const std::pair<A, B>& p){
cerr << "(";
__print(p.first);
cerr << ", ";
__print(p.second);
cerr << ")";
}

// tuple
template <typename Tuple, size_t... I>
void __print_tuple(const Tuple& t, std::index_sequence<I...>){
using swallow = int[];
(void)swallow{0, ( (cerr << (I == 0 ? "" : ", "), __print(std::get<I>(t))), 0)...};
}

template <typename... Args>
void __print(const std::tuple<Args...>& t){
cerr << "(";
__print_tuple(t, std::index_sequence_for<Args...>{});
cerr << ")";
}

// C-arrays
template <typename T, size_t N>
void __print(const T (&arr)[N]){
cerr << "{";
for(size_t i = 0; i < N; ++i){
if(i) cerr << ", ";
__print(arr[i]);
}
cerr << "}";
}

// ==================
// 容器
// ==================

template <typename T>
typename std::enable_if<
is_iterable_v<T> and
!std::is_same<T, string>::value and
!std::is_arithmetic<T>::value
>::type
__print(const T& v){
cerr << "{";
bool first = true;
for(const auto& x : v){
if(!first) cerr << ", ";
first = false;
__print(x);
}
cerr << "}";
}

// ==================
// 变量名拆分
// ==================

inline void trim(string& s){
while(!s.empty() and isspace(s.back())) s.pop_back();
size_t pos = 0;
while(pos < s.size() and isspace(s[pos])) ++pos;
s.erase(0, pos);
}

inline std::vector<string> split(const string& s){
std::vector<string> res;
string cur;
int paren = 0;
for(char c : s){
if(c == ',' and paren == 0){
trim(cur);
res.push_back(cur);
cur.clear();
}
else{
if(c == '(') ++paren;
if(c == ')') --paren;
cur += c;
}
}
trim(cur);
if(!cur.empty()) res.push_back(cur);
return res;
}

// ==================
// 输出逻辑
// ==================

inline void debug_out_impl(
const std::vector<string>& names,
size_t) {
cerr << '\n';
}

template <typename T, typename... Rest>
void debug_out_impl(
const std::vector<string>& names,
size_t idx,
T&& value,
Rest&&... rest
) {
if (idx < names.size()) {
cerr << names[idx] << " = ";
__print(std::forward<T>(value));
if (sizeof...(rest)) cerr << ", ";
}

debug_out_impl(names, idx + 1, std::forward<Rest>(rest)...);
}

template <typename... Args>
void debug_out(
const std::vector<string>& names,
Args&&... args
) {
debug_out_impl(names, 0, std::forward<Args>(args)...);
}

} // namespace dbg

// ==================
// 宏入口
// ==================

#define debug(...) do { \
std::cerr << __FUNCTION__ << ":" << __LINE__ << " "; \
auto __names = dbg::split(#__VA_ARGS__); \
dbg::debug_out(__names, __VA_ARGS__); \
} while(0)

#define dbg(...) do { \
auto __names = dbg::split(#__VA_ARGS__); \
dbg::debug_out(__names, __VA_ARGS__); \
} while(0)
  • 标题: 算法竞赛自用 debug 库
  • 作者: Coast23
  • 创建于 : 2026-02-14 15:03:17
  • 更新于 : 2026-02-20 00:09:20
  • 链接: https://coast23.github.io/2026/02/14/算法竞赛自用-debug-库/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
算法竞赛自用 debug 库