我的 debug 代码发展历程

不知道为啥写了这一篇,可能是因为临近退役,想要 save 一些自己感觉有意义的事物吧。

在最一开始,我的 debug 就是最普通的,在需要输出的地方把变量输出,查看。
下面的代码就是一个例子。

for(int i=1;i<=m;i++){
  int r=find(u,w)+1;cout<<r<<endl;//
  if(r>find(v,w)){
    auto it=f[v].lower_bound(w);f[v][w]=r;
    while(it!=f[v].end()&&it->second<=r) f[v].erase(it++);
  }ans=max(ans,r);cout<<ans<<endl;//
}

后来应该是在 \(\color{Red}{Cotsheep}\) 那学到了关于 宏定义 的一些芝士,当时也就将其作为一个打 tag 的手段吧。

#define dbg cout<<"FUCK!\n";

之后在非常短的一段时间里,使用了一个 CF 上魔改的 debug 代码。

vector<string> vs(string s){
  s+=',';vector<string>r;while(!s.empty())
    r.pb(s.substr(0,s.find(','))),s=s.substr(s.find(',')+1);return r;
}void debug_out(vector<string>__attribute__((unused)) args,
__attribute__ ((unused)) int idx,__attribute__((unused))int ln){cerr<<endl;}
template <typename Head, typename... Tail>
void debug_out(vector<string> args, int idx, int ln, Head H, Tail... T){
  if(idx>0) cerr<<",";else cerr<<"Line("<<ln<<") ";
  stringstream ss;ss<<H;cerr<<args[idx]<<'='<<ss.str();
  debug_out(args,idx+1,ln,T...);
}
#define out(...) debug_out(vs(#__VA_ARGS__), 0, __LINE__, __VA_ARGS__)

后来因为某些原因,被弃用了。

很长一段时间里,没有再次用 debug。
在某一天,我翻 \(\color{Gold}{kk19212}\) 的某题代码时,发现了如下部分。

namespace LOCAL{
#ifdef DEBUG
#define react puts("reacting now !")
#define debug(x) cout << #x << " = " <<  x << "\n"; 
#define debug2(l,r) cout << "[" #l "," #r "] = [" << l << "," << r << "]\n" ;
#define debug3(x,y,z) cout << "{" #x "," #y "," #z "} = " << "{" << x << "," << y << "," << z << "}\n" ; 
#define debug_vec(v) cout << #v": size= " << v.size() << "\nelement: " ; for(auto p:v)  cout << p << " " ;
#define FileIO(x) 
#else 
#define react
#define debug(x) 
#define debug2(l,r)
#define debug3(x,y,z)
#define debug_vec(v)
#define FileIO(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
#endif 
}

于是我大抵了解了一下关于 #ifdef,#else,#endif 之类的信息。
也就改装得到了,我用过一段时间的 debug 代码。

与此同时,我想起了 CF上的一个讨论
于是我最后得到了这样的代码。

namespace LOCAL{
#ifdef DEBUG
#define got cout<<"get here"<<'\n';
#define cut cout<<"-------------------------\n";
#define dgvi(v) cout<<#v": size= "<<si(v)<<"\nelement: ";for(auto p:v) cout<<p<<" ";cout<<'\n';
#define dgar(x,s) cout<<#x": len= "<<s+1<<"\nelement: ";L(i,0,s) cout<<x[i]<<" \n"[i==s];
#define dge(x,n) int _=0;L(i,0,n) _+=si(x[i]);cout<<"There are "<<_<<" edges in "<<#x<<'\n';\
                 L(u,0,n) for(int v:x[u]) cout<<u<<' '<<v<<'\n';
#define dg(...) W(#__VA_ARGS__,__VA_ARGS__)
template<typename ...Args>
void W(string X,Args&&...V){cout<<X<<" = ";string D=""; (...,(cout<<D<<V,D=", "));cout<<'\n';}
 
#else 
#define got
#define cut
#define dgvi(v)
#define dgar(x,s)
#define dge(x,n)
#define dg(...)
#endif 
}using namespace LOCAL;

但我有种感觉,这样写有很多代码在干几乎不会用到的情况。
先插入一个有点关系的事情:
就是我在 \(\color{Gold}{kk19212}\) 的代码中,还捞到了如下的代码:

namespace IO{
#define MAXSIZE (1<<20)
char *IO___p1=NULL,*IO___p2=NULL,IO___buf[MAXSIZE];
#ifdef DEBUG
#define gchar() getchar()
#else
#define gchar() (IO___p1==IO___p2&&(IO___p2=(IO___p1=IO___buf)+fread(IO___buf,1,MAXSIZE,stdin),IO___p1==IO___p2)?EOF:*IO___p1++)
#endif
inline void read(char *x){
    char ch=gchar();
    while(ch==' '||ch=='\n'||ch=='\r')ch=gchar();
    do *x++=ch,ch=gchar(); while(ch!=' '&&ch!='\n'&&ch!='\r');
    *x='\0';
}
template<typename _Tp>inline void read(_Tp &x){
    x=0;char ch=gchar();
    for(;!isdigit(ch);ch=gchar());
    for(;isdigit(ch);ch=gchar())x=(x<<1)+(x<<3)+(ch^48);
}
template<typename _Tp>inline void nread(_Tp &x){
    x=0;_Tp f=1;char ch=gchar();
    for(;!isdigit(ch);ch=gchar())ch=='-'&&(f=-1);
    for(;isdigit(ch);ch=gchar())x=(x<<1)+(x<<3)+(ch^48);
    x*=f;
}
template<typename _Tp,typename ...Args>
inline void read(_Tp &x,Args &...args){read(x),read(args...);}
template<typename _Tp,typename ...Args>
inline void nread(_Tp &x,Args &...args){nread(x),nread(args...);}
template<typename _Tp>inline void wrt(_Tp x){
    if(x<0)x=-x,putchar('-');
    if(x>9)wrt(x/10);
    putchar(x%10+48);
}
inline void wrt(char ch){putchar(ch);}
inline void wrt(char *s){while(*s!='\0')putchar(*s++);}
inline void wrt(const char *s){while(*s!='\0')putchar(*s++);}
template<typename _Tp,typename ...Args>
inline void wrt(_Tp x,Args ...args){wrt(x),wrt(args...);}
#undef MAXSIZE
}

毕竟我是 cin 流同步一关,天塌地陷与我无关的人,于是我把代码改成了这样:

namespace IO{
template<typename T>void rd(T &x){cin>>x;}template<typename T>void wr(T x){cout<<x;}
template<typename T,typename ...Args>void rd(T &x,Args &...args){rd(x),rd(args...);}
template<typename T,typename ...Args>void wr(T x,Args ...args){wr(x),wr(args...);}
}using namespace IO;

后来还要提到这事。

但我又发现,在调试容器时并不尽人意。
我尝试寻找可用的代码,但一直难以找到较为容易理解正常的。

最后,我在一篇 CF 的官方题解中找到了我满意的答案。

里面有如下的代码:

template<class A, class B> ostream& operator <<(ostream& out, const pair<A, B> &p) {
	return out << "(" << p.x << ", " << p.y << ")";
}
template<class A> ostream& operator <<(ostream& out, const vector<A> &v) {
	out << "[";
	fore(i, 0, sz(v)) {
		if(i) out << ", ";
		out << v[i];
	}
	return out << "]";
}

其实就是重载了 << 流(?
使之能够像普通的变量那样输入输出。
于是我照样重载了几个常用容器的运算符,使之能照常输出。

最后,我发现了一个 __Line__ 的用处,就是输出所在程序行数,使我不容易找丢位置。

于是,我得到了最后的代码:

#define DE
template<typename ...Args>
void W(string X,Args&&...V){cerr<<X<<"]=[";string D=""; (...,(cerr<<D<<V,D=","));cerr<<"]\n";}
#ifdef DE
#define tam cerr<<1e3*clock()/CLOCKS_PER_SEC<<" ms "<<(&y_y-&x_x)/1024.0/1024.0<<" MB\n",0
#define dg(...) cerr<<"Line("<<__LINE__<<"):"<<" [",W(#__VA_ARGS__,__VA_ARGS__)
#else
#define dg(...)
#define tam 0
#endif

namespace IO{
template<class A,class B>ostream&operator<<(ostream&X,const pair<A,B>&p)
{return X<<"{"<<F(p)<<","<<S(p)<<"}";}
template<class A>ostream&operator<<(ostream&X,const set<A>&v)
{X<<"{";for(auto i:v){if(i!=*v.begin()) X<<",";X<<i;}return X<<"}";}
template<class A,class B>ostream&operator<<(ostream&X,const map<A,B>&v)
{X<<"{";for(auto i:v){if(i!=*v.begin()) X<<",";X<<i;}return X<<"}";}
template<class A>ostream&operator<<(ostream&X,const vector<A>&v)
{X<<"{";L(i,0,si(v)-1){if(i)X<<",";X<<v[i];}return X<<"}";}
template<typename T>void rd(T &x){cin>>x;}template<typename T>void wr(T x){cout<<x;}
template<typename T,typename ...Args>void rd(T &x,Args &...args){rd(x),rd(args...);}
template<typename T,typename ...Args>void wr(T x,Args ...args){wr(x),wr(args...);}
}using namespace IO;

举个使用例子:

int main() {
    vector<int> v({1, 2, 3});
    map<int, int> mp;
    int x =10;
    int y =11;
    mp[12] = 13;
    mp[24] = 32;
    dg(x, y, v, mp);

    return 0;
}

output:

Line(78): [x, y, v, mp]=[10,11,{1,2,3},{{12,13},{24,32}}]

还有一部分是测程序运行的时间和空间的。

cerr<<(ld)(clock()-S)/CLOCKS_PER_SEC<<" s ";
//......

int main(){
  clock_t S=clock();
//......

}

原理就不解释了。输出的以秒为单位,想输出毫秒乘上 1000 即可。

cerr<<(&y_y-&x_x)/1024.0/1024.0<<" MB\n"

在主程序外分别定义两个变量,把所有数组夹在中间,即可借此求出空间。
对于 #define int long long 人来说,是必要的救命技巧。

以上。

posted @ 2022-11-14 21:36  AIskeleton  阅读(29)  评论(0编辑  收藏  举报