作为一个正派的程序员,对于程序中宏的使用是:无必要,勿增宏 。(奥卡姆剃刀了解下)原则的前提初心是作为一个正派的程序员。但如果你想成为一个成功的程序员呢? 宏也许能帮你更上一层楼。某前任公司,常年996(为此钱没赚多少,发际线上升了2cm不止。悔不该当初啊,你问我钱赚了能花,发际线上去了能下来吗? 你看看全球各大富豪,秃顶难看他们不知道? 扯多了...),那家公司虽然说大家常年996,但也不是所有人996,有个“大佬”(注意大佬我打了引号),他是965!CTO、架构师可能离我们还有点远(毕竟还没学全),有人可能会问他为何能脱身事外,独享965。他是老板亲戚还是家里几套房? 都不是!他只是比你们先学了如何把“宏”玩出花来! 全公司的宏都是他写的,正派的我虽然不知道为啥需要那么多宏,但是领导肯定,我也不得不用。同时开始学会从另一个角度看待宏。
宏的作用可以归纳为以下几点:
1、当一个对应N行代码的宏,被替换在某个位置时,变为一行代码时,他就变得不可调试了。非写代码熟悉业务的当事人不可解。江湖地位+1 :](方括号的笑脸比如 :) 开心)。
2、嵌套宏,来一起绕圈圈。 江湖地位+1 :]。
宏这个东西在大多数公司都属于灰色地带或者禁品,虽然作用很大,但是如何在实际工作中引入并推广是需要技巧、掌握时机后量力而行的。一个引入不当可能身败名裂,地位不保。所以接下来我会从易到难、循序渐进的讲解一些宏的引入。
1、基础:
如果你是个UI仔,不管做什么UI的,宏的引入想来不是个问题,类似QT中的Q_OBJECT之类的宏在项目中遍地开花是难以避免的。但如果不是呢,如果某些公司严格规范代码呢?此刻你可能需要想办法说(shui 四声)服领导允许你在项目中使用宏,并给他展示宏的一些积极特性。
相比无处不在magic number,领导更容易接受宏:
// 像这样
#define X_FLAG _T("flag")
#define X_MAGIC_NUM (100)
// 千万别这样,因为新人很容易把你的代码读懂并修改。 新人:这块代码是我的了.jpg,
// 江湖地位 -1。(重要的事我之后会一直说!)
const tstring g_flag = _T("flag");
const int g_magic_num = 100;
注意事项:
前期创业之初一定要,警惕把握好度,最好在跟领导商量取得同意之后,再在空闲时间处理。切入点,项目magic num之类的东西很多,积极主动的在空闲时间整理并引入宏。君子终日乾乾,夕惕若,厉无咎。
引入功能函数。
// 像这样
#define ISFLOATEQUAL(doublevalue, equalvalue) (abs(doublevalue - equalvalue) < 0.0000001)
// 你还可以依此类推,写个EQUAL系列的宏出来,比如ISDOUBLEEQUAL 之类的
...
// 千万别这样
bool is_equal(float val1, float val2){...}
bool is_equal(double val1, double val2){...}
...
// 更有甚者写个template一劳永逸
template<class val_type>
bool is_equal(val_type val1, val_type val2, val_type precision = 0.0000001)
{
return std::abs(val1 - val2) < precision;
}
没有什么是一蹴而就的,你必须有长期艰苦奋战的心理准备。有诗为证: 王莽谦恭未篡时...
宏(文本替换)开始发挥他应有的作用。大家也开始接受它的存在之后,我们择机将成果扩大。在所有人都被枯燥的class成员变量、get、set函数定义折磨过之后,## 将被引入项目。
// 像这样
#define x_attr(type, name) \
public:\
type m_##name; \
void set_##name(const type& val) \
{\
m_##name = val;\
return ;\
}\
type get_##name() const \
{\
return m_##name;\
}
// 千万别... ...千万别犯傻!
....
开始尝到宏的甜头? 到目前为止,已经成功了小半了。
当大家开始学会在代码里面用各种宏,秀各种技术的时候,会发现个问题,宏替换基本上产会将代码生成在同一行,这样如果代码量大,逻辑复杂的时候,一旦出问题,你没法动态调试。这个时候很关键,因为有些人可能会对宏的广泛应用提出质疑并提出不同的解决方案(江湖地位 -1)。怎么办? 写日志予以还击啊。。。
日志系统实现好了之后,单独的一个写日志其实并不能满足各种复杂场景的花式写日志。接下来便可以进一步的引入宏。
很多时候我们不光要在数据异常的时候assert,我们还要写日志,把它记录下来。代码千篇一律,每一个地方都加的话,工作量不小(体力活),有什么办法高效解决问题,并兼容之后的需求变更? 宏可以。
// 像这样
#define warn_log printf
#define to_string_(name) #name
#define x_attr(type, name) \
public:\
type m_##name; \
void set_##name(const type& val) \
{\
m_##name = val;\
return ;\
}\
type get_##name() const \
{\
return m_##name;\
}
#define x_assert(condition) \
warn_log("%s = %d\n", to_string_(condition), condition);\
if(!condition) \
{ \
Do what U want. \
}
// 千万别,千万别定义各种函数(因为打断点它特么太方便了!) 新人:你的代码归我了.gif
void x_assert(bool condition){ ... }
温馨提示:现在你已经学会了宏的各种用法,并应用于实际当中了,要学会适当的拓展宏的使用,比如你能用宏定义属性,那为什么不能用宏定义各种通用接口呢?像Q_OBJECT一样。以至让自己让别人相信,宏比继承香。再者,以上举例都基本上是单层的调用。在接下来类的工作中亦可择机一层一层的嵌套。比如上面asser宏中我们使用了log宏,在出现异常的时候写个日志,再基础不过的需求了,为什么不写?
宏的基础应用基本讲完了,关于如何实施,这个需要把握好度,所谓:应用之妙、存乎一心,不外如是。
自此,千秋大业完成了一半,随着你日日夜夜的不断付出,江湖地位不断攀升,新来的小盆友天天在你屁股后面溜须拍马,你的名字也不再是小梁、小郑了,领导都叫你梁工。所以你开始满足了?冷静点、这是悲剧的开始,自古功高盖主的没一个好下场,接下来的日子小朋友们以学习梁工代码为由不断地蚕食你的领地,顺带夸一句梁工代码写得好,清晰易懂。我们都会了。 领导听到这话之后,一想、项目基本稳定了,这个梁工拿着那么高的工资,维护一块小盆友都能维护的代码,是在是太屈才了、打发了吧...
所以,一定要看好自己的一亩三分地,让别人心生敬畏,不敢来此搞事情。这很重要...
2、进阶:
基础篇其实并没有啥用,只是为了引入宏而已,或者说只是个开局。在现有江湖地位基础上,如何稳坐钓鱼台才是重点。所以我们要进阶。
如何进阶呢?常见但并不高明的进阶方式有:
// 命名,改成让人看着就难受的名字,比如这种驼峰加下划线组合:
#define X_ZeroMemory_Normal(p, nSize) memset((p), 0, (nSize))
// 扩散,嗯,像瘟疫一样扩散,凡是能想到的都给他换成宏,比如:
#define StdMax(a, b) (std::max)((a), (b))
// 嵌套,一层套一层,万万不可操之过急,一定要给队友适应时间,以及发挥空间。
// 队友发挥空间?给他们自主定义宏的权利,但必须在你的基础宏的基础上,充分释放他们
// 的个性,州官放火的时候也要给百姓点灯的机会,所谓雨露均沾?
#ifdef X_Debug
# ifdef X_Win32
# define X_Warn (!X_Warn_Win32(__FILE__, __LINE__) || (__debugbreak(), 0)) //Win32
# else
# ifdef X_Apple
# define X_Warn { X_Warn_WriteLog(__FILE__, __LINE__); X_Assert(false); } //Apple
# else
# define X_Warn X_Warn_WriteLog(__FILE__, __LINE__) //Other
# endif
# endif
#else
# define X_Warn X_Warn_WriteLog(__FILE__, __LINE__)
#endif
#define X_Assert X_Warn
// 个性化定制拓展,比如:
#define ZeroMemory_Normal(p, nSize) memset((p), 0, (nSize))
#define ZeroMemory_Array(arrayVar) X_ZeroMemory_Array(arrayVar)
#define ZeroMemory_Struct(stVar) X_ZeroMemory_Struct(stVar)
#define ZeroMemory_Type(p, typeName) memset(p, 0, sizeof(typeName))
// 再比如assert系列还能这样:
#define X_Assert_Return(r) X_Warn_Return(r)
#define X_Assert_ReturnVoid X_Warn_ReturnVoid
#define X_AssertEx(b) X_If_Warn(b)
#define X_AssertEx_Return(b, r) X_If_Warn_Return(b, r)
#define X_AssertEx_ReturnVoid(b) X_If_Warn_ReturnVoid(b)
// 当你江湖地位逐渐稳固的时候,你还可以这样(指鹿为马,颠倒黑白?):
#define x_assert(condition) \
if(condition) \
{ \
正统的程序员看到assert的时候,想到的是如果条件不成立则为异常, \
然后处理异常,但你**在能力可及的时候,把它反过来**,小盆友对此望而生畏。\
}
上面都是小技巧,虽然是小技巧,但如果你一整套实施下来,90%的进度基本能达成了。为什么是90%?因为代码质量可能就此一落千丈,对你的声望还是有一定影响的。还是那句话:应用之妙,存乎一心。一定要把握好度。那有什么挽救方法呢? (我既然提出来了肯定有。)比如:pub-sub,producer-consumer之类的基于事件驱动的模型项目当中,用宏实现一个EventEmitter,然后应用于项目当中,江湖地位+10;关于如何用宏实现EventEmitter,由于代码量有点大(意味着难以看懂),如果有人有需求,可以私下找我索要(我整理好之后会考虑放到github上去)。
至此整篇关于宏的部分就讲完了,再次强调举一反三,活学活用,还要把握好度,写多少宏,怎么写宏,一定要先看看自己江湖地位有多高。考虑到有些人并不能做到活学活用举一反三(不然怎么混得这么惨?),我会在我github弄好之后,逐步的放上一些项目组常用的宏。以便大家摘抄拓展。