背景
用过Go、py一类的可能都有所感受,对于不定参数的使用很多时候代码中真的很爽。让你原地起飞。
遗憾的是,c或者c++关于不定参数的使用实际是基于参数栈计算偏移获取值的一个过程。不过这也有好的一点,效率还是不错的。
其中最大的问题莫过于既然是计算便宜我们在不定参函数内部是不知道参数的个数的,因此我们必须使用参数也好传值或者其他间接传值让函数内部的参数长度得到指定,之后才可以愉快地取参。
正题开始
但是,c或者cplus里存在一个叫做宏的东西。这玩意儿再加上多态,说实话,其实撸代码已经很爽了。只是缺少一些轮子罢了。
请君观看
#define ARG_T(t) t //解决VC编译错误
#define ARG_N(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,N,...) N //截取并返回第四个参数,这里限制了可变参数计算的范围[1,3]
#define ARG_N_HELPER(...) ARG_T(ARG_N(__VA_ARGS__)) //辅助宏
#define COUNT_ARG(...) ARG_N_HELPER(__VA_ARGS__,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) //返回可变参数个数
啊不是,拿错了。我们看一下它的数量简化版。
//假如这是你的逻辑函数
PUNICODE_STRING NewUTFString(int SourceCount,wchar_t* ...)
{//...假设的一个逻辑函数
}
#define ARG_T(t) t //解决VC编译错误
#define ARG_N(a1,a2,a3,a4,N,...) N //截取并返回第四个参数,这里限制了可变参数计算的范围[1,4]
#define ARG_N_HELPER(...) ARG_T(ARG_N(__VA_ARGS__)) //辅助宏
#define COUNT_ARG(...) ARG_N_HELPER(__VA_ARGS__,4,3,2,1,0) //返回可变参数个数
//该宏封装函数
#define XX(...)\
NewUTFString(COUNT_ARG(__VA_ARGS__),__VA_ARGS__)
//...
//使用宏调用
XX(L"A", L"B");
//...
//直接调用
NewUTFString(2, L"A", L"B");
注意看,我们使用XX宏封装过后不需要再传入参数的个数,可以直接使用。而直接使用的时候,则需要传入参数个数再依次传入参数,这样有个十分不好的地方。
当你某个时候做改动回来增加参数时,需要1、2、3开始数参数个数,然后写入一个值,多呆哦。
而这个宏则完美解决了我们需要处理个数的问题,宏替换发生在编译前。所以不仅不会降低效率,还能提高开发效率、降低出错率、提高易用性。
原理剖析
细心的博友们可能已经发现了,萌新小伙伴们可以还比较蒙。没关系,我们开始一步步剖析。对宏进行一次模拟展开。
未展开时:
//...
//使用宏调用
XX(L"A", L"B");
第一步替换XX宏:
NewUTFString(COUNT_ARG(L"A",L"B"),L"A",L"B");
第二部展开COUNT_ARG宏:
NewUTFString(ARG_N_HELPER(L"A",L"B",4,3,2,1,0),L"A",L"B");
//太乱了,我们只把计算数量这抄下来好了
ARG_N_HELPER(L"A",L"B",4,3,2,1,0)
第三步展开ARG_N_HELPER宏
ARG_T(ARG_N(L"A",L"B",4,3,2,1,0))
此时出现两个宏,这刚学编程就知道吧。运算优先级。所以先展开里面的
ARG_N 宏:
//调用行为
ARG_N(L"A",L"B",4,3,2,1,0)
#define ARG_N(a1 ,a2 ,a3,a4,N,...) N //宏的定义
|-------| <---看这N对应的数字替换的值
(L"A",L"B",4 ,3 ,2,1,0) 2 //值展开
//参数依次占据a1,a2,a3,a4,N,之后的参数不被使用
到这一步 我们的一堆参数就被替换成一个数字的,而这个数字一定是你的参数个数。
十分神奇,这个计算过程就像是是一个占位的长条被传入的参数推动到某个数字的感觉。
这。
后记
编程之中有各种奇淫巧技,这些巧技会让你的代码有高级感,同时这些技巧的背后也透露着更深层的解决问题的思路,路漫漫其修远,我辈逆流而求索...