超实用的缺省源/代码模板!

· · 个人记录

前言

如果你还在用这种方式调试:

int a=1,b=2,c=3;
cout<<a<<' '<<b<<' '<<c<<endl;

那简直太浪费时间了,并且输出的信息只有孤零零的三个数字,像这样:

1 2 3

更新2-实战效果镇楼

下面是普通例子

但你有没有想过你其实可以这样调试:

int a=1;
double b=3.14;
string c="tql";
pot(a,b,c);
//宏的名字可以随便换,叫pot只是防止不必要的命名冲突,谐音put

不但打起来快了很多,而且输出信息竟然是这样的:

Line 21 : a,b,c : 1 3.14 tql

你没有看错,不但有 pot() 执行的行数,甚至还有变量名!

最关键的是,这不是什么先进的编辑器,这只是用 C++14 的语法打出来的 15 行模板的效果!甚至核心部分只有 7 行,对的,你没听错,核心部分只有 7 行!

而且授人以鱼不如授人以渔,这个还可以根据自己的审美定制的,也可以扩展更丰富的功能!这意味着,你完全可以把这项技能带到考场上,打一个模板四道题都可以用,节省大量的调试时间!

并且通过删除 pot 宏,你还可以因为没有删除调试信息造成的 WA 悲剧!因为你删掉了 pot 宏后,如果还有调试语句,根本就无法编译通过,完美避免这样的惨剧!

如果你还担心下面这样的调试语句无法删除,下文还可以教你一招!

int work(int x){
    if(x!=3)return;
    //为了单独调试x=3而忘记删除的惨剧,没错就是作者犯过的错误
    //...
}

模板

废话不多说,直接上模板,C++14 标准的:

#include<bits/stdc++.h>
#define err() cout<<"err "<<__LINE__<<endl,exit(0)
#define pot(args...) \
GPT(#args),cout<<"  Line "<<__LINE__<<"\t: ", \
PPT(args),cout<<"\n\n"
#define F(i,s,t) for(ll i=(s);i<=(t);i++)
using namespace std;
void PPT(){}
template<typename TYPE,typename... TYPES>
void PPT(const TYPE& x,const TYPES&... y){
    cout<<x<<' ';
    PPT(y...);
}
void GPT(string nam){cout<<setw(29)<<nam;}
typedef long long ll;

int main(){

    return 0;
}

不建议压行,因为这代码扩展性极强,不是固定的!只用改一下头部代码,所有调试代码都跟着改!例如你多加一个 \n,所有调试语句都多换一行!

效果杠杠的

效果可以测试以下代码:

int main(){
    ll a=6,b=6,c=6;
    pot(a,b,c);//自动打印 
    err();//报错并结束程序
    cout<<"这行不会被执行"<<endl; 
    return 0;
}

输出信息(行数信息可能不同):

没错,就是原汁原味的自动智能打印,打印行数,打印变量名,可以直接用在考场上。而且还可以自己修改 #define 的内容,定制输出格式,扩展性极强

各功能介绍

可以把宏当作函数一样用。

我这个版本的思路大概就是先输出变量名,再输出行信息,再输出实际参数值,经过实战,我发现这是最舒服的调试方式,因为可以直接对照着 Line 看,Line 看不懂再往前看变量信息。

pot 宏

#define pot(args...) \
GPT(#args),cout<<"  Line "<<__LINE__<<"\t: ", \
PPT(args),cout<<"\n\n"

通过宏来解析,直接把行数和变量名附带进打印函数,不了解宏的话可以参考这篇文章:【洛谷日报】C++中偷懒利器——宏。

这个宏是专门调试用的,像python一样,支持任意多参数,而且还会自动格式化,非常简单快捷。

err 宏

#define err() cout<<"err "<<__LINE__<<endl,exit(0)

报错并终止程序,比 exit(0); 高级的一点是会告诉你在第几行执行的 err() 退出的程序,可以快速定位并调试,效果像这样:

err 20

F 宏

#define F(i,s,t) for(ll i=(s);i<=(t);i++)

F(i,s,t) 就是对普通循环做一个缩写,很多循环都属于这种朴实无华的循环,可以节省码字时间并让代码看上去更简洁。

题外话

局部函数里的调试代码,做标记可以直接用 0; ,并开启 -Wall 编译命令,会警告这是无效命令,像这样:

void work(ll x){
    0;if(x!=3)return;
    //上一行是调试代码,前面加个0; 就可以被警告 
    //...
}
int main(){
    ll n=10;
    for(ll i=1;i<=n;i++)work(i);
    return 0;
}

警告信息:

[Warning] statement has no effect [-Wunused-value]

这样就能有效防止因为调试代码而导致的WA了。

PPT 函数

void PPT(){}
template<typename TYPE,typename... TYPES>
void PPT(const TYPE& x,const TYPES&... y){
    cout<<x<<' ';
    PPT(y...);
}

这个函数是用来格式化打印的,是辅助 pot 宏打印的。名字瞎起同样为了防止命名冲突,支持传入任意多个参数,效果像这样:

PPT(1,"23",4.56,bitset<8>(7));

/*输出:
1 23 4.56 00000111
*/

关于他的原理:我们都知道可以写多个函数,带有不同的参数,编译器会帮你选择用哪个参数,像这样:

ll mod10(ll x){return x%10;}
ll mod10(string x){return x[x.size()-1]-'0';}
int main(){
    cout<<mod10(123)<<endl;
    cout<<mod10("123")<<endl;
    return 0;
}

代码之所以能够成功编译,就是因为编译器就会在执行代码的时候自动帮我们选择合适的那个函数。

那我们要实现传入任意多个任意种类的参数的话,我们就要用两个函数,一个是啥都没有,相当于递归的最后一层,一个是递归函数,传入一个参数和一串参数。

然后编译器就会帮我们像剥洋葱一样的一层层把这些参数都剥掉,剥到最后什么都没有就会执行另一个啥都没有的函数,然后返回。

然后大体上我们就实现了这样一个功能,我们还需要借助一些 C++ 的语法功能诸如 template 之类的来实现传入任意种类的参数,这里我就不再赘述了。反正代码也不长,就 5 行,实在不能理解的背下来也不难。

GPT 函数

void GPT(string nam){cout<<setw(29)<<nam;}

同样是瞎起的名字防止命名冲突,用来格式化输出变量名,统一在右边,传入 string 并通过 setw格式化输出。

更新1-实战体验

上面主要说的好处是提高了 效率正确率 ,经过我个人的实战,发现这玩意儿对 心态 有特别大的改善。

因为我调试的速度真的很快,一个 bug 往往我能够在很短的时间内反应过来,所以我不慌,心态 很稳,真的非常稳。

我刚刚连切三道 96行76行72行 的程序,有点膨胀了,感觉百行以内的程序只要思路对的 40分钟 就能搞定。

虽然三个写的全都是bug,但我调试的速度那是真得快啊,毫不夸张,配 200+ 的码速,直接起飞,所以都是 40分钟 内一遍过,使用的还是压行后的模板。

我觉得很重要的一个原因在于这里,看下面这样一个代码:

pot(i,j,X[i],X[j],len);//23个字节,只用了5秒

这是我实战过程中写的,如果换成我之前写的版本:

cout<<666<<' '<<i<<' '<<j<<' ';
cout<<X[i]<<' '<<X[j]<<' '<<len<<endl;
//没错我还要折行,算上换行符70个字节,要12秒左右,还很长,影响观感

朴素的版本这三个坏处对心态有很大的影响:

如果要写一个完全像 pot 那样的,耗费的时间可以说几乎就要是 30秒 了,而且行数还不好维护,写成这样又很乱其实,影响心态。

所以你们知道我为什么膨胀了吧?因为调试代码的成本降低了,我就可以写更多调试代码,看得更清楚,DeBug 更快,心态更稳!

当然,实战体验因人而异,我说得也不一定对,这样不一定是最高效的方式,实践出真知,还望大家多多实践,多多探索!如果有什么好的想法欢迎与我私信交流!

更新2-实战体验

不说了,开头那图久久地震撼了我:

当然,为了保证三位数代码的显示效果,我特意拉长了代码的。

更新3-关于评论区

assert(0); 也是一个不错的代替 exit(0); 的方式,还能显示详细信息,像这样:

可以改成这样:

#define err() assert(0)

也可以用原来的样式,毕竟原来的样式相对简略一点,看个人喜好吧。

更新4-超级丰富的扩展性

哇没得说,这玩意儿扩展性真的太强了!

比如说,你现在想打印一个队列的 push 操作,有什么方案呢?

第一种,直接造个函数,把原来的 q.push() 都删掉,像这样:

void Push(ll q_id,ll q_k){
    pot(q_id,q_k);
    q.push({q_id,q_k});
}

效果:

第二种方案:

啊,还有一种更加普适性的方案,就是定义个新的宏,规定第一个参数是解释:

#define pota(x,args...) \
GPT(#x),cout<<"  Line "<<__LINE__<<"\t: ", \
PPT(args),cout<<"\n\n"
pota(q,x,y);//第一个写解释,可以随便写,不一定要是某个变量的名字

效果:

啊,还有更多操作,但也有更多可能性,熟练掌握宏和 C++14 的语法的话,完全可以玩出花来!

后记

至此你就已经看到了一种相对于朴素的调试方法更加高效、智能的方法,还不赶紧把板子粘下来好好研究研究?

这可是真正考场上能帮你节省时间的板子,而且打一遍每道题都能用,毫不夸张,比快读板子实用多了!

虽然作者参考了不少文献,但这个模板和调试方法确实是作者自己研究出来的,是原创的,花了不少心血呢!

所以如果你觉得这个模板不错的话,还请点个赞再走吧,希望这个模板能帮到你!QwQ

Reference

err 宏 和 pot 宏的灵感源自:

DEL 的灵感源自:

PPT 函数的灵感源自:

F 宏的灵感很早就有,也不是原创的,已经无法考证。

文章源码

本文最后更新时间:2022-01-07 11:15

文章源码在这里:洛谷云剪切板,点击查看源码即可。

原文链接:超实用的缺省源/代码模板!。

转载还请注明原文链接和作者信息,谢谢!