c++ 预处理的图灵完备之引言
副标题[/!--empirenews.page--]
我们还是来讨论c++吧,这几年在c++里面玩代码自动生成技术,而预处理是不可避免,也是不可或缺的重要工具。虽然boost pp预处理库在宏的运用上很是完善,但是代码也太多了,而且代码很不好理解,对此,不免让人疑惑,有必要搞得那么复杂,搞那么多代码吗?并且,看了boostpp的使用接口后,感觉写得很不干净,也不好组合。因此,重新做了一套预处理的轮子。以下的代码,假设在msvc2013以上的版本运行,反正很多人用MSVC的,装逼的自当别论,造出来的轮子,倾向于先支持msvc。 首先,我们定义一个宏,用来给把入参变成字符串,咦,这个事情也太easy了,但是,在此,感觉,还是有必要废话多解释一下。以下代码惯例都是,所有可用的宏函数都是以PP开头全部大写,而以_ZPP开头的全部都是内部实现,其实还可以做得更难看一点。因为宏函数是全局的,没有作用域的概念,并且只是单纯的文本替换,死的时候,还不知道怎么死,所以,必须谨慎对待。像是windows.h头文件那样,直接用min,max作为宏的名字,虽然用起来很方便,但也不知道制造了多少麻烦,所以,很多时候,包含windows.h时,第一件事情就是undef min和max。 以下的代码,可以随便在某个工程下,随便建立一个cpp后缀名的源文件,然后按CTRL+F7编译,不需要F5,就可以看到运行的效果,如果编译通过,就说明宏基本上正确,测试代码越多,准确性就越高。当然,你们也可以通过设置源文件的属性,让msvc生成预处理后的文件,然后用记事本打开那个文件观看。 #define PP_TEXT(str) _ZPP_TEXT(str) #define _ZPP_TEXT(str) #str 在c++预处理宏中,操作符#是将后面跟随的表达式加上两个双引号,也就是字符串。PP_TEXT(str)不是直接定义成#str,而是通过调用_ZPP_TEXT(str),然后在那里才将入参变成字符串,显得有点辗转,有点多此一举,但,其实是为了支持宏的全方位展开,也就是入参str本身也存在宏调用的时候,纯属无奈。比如,如果这样实现 #define PP_TEXT(str) #str 那么,对于下面的情况, #define AAA aaa PP_TEXT(AAA),结果将是"AAA",而不是"aaa"。因为宏操作符直接是将入参变成字符串,没有让入参有一点点回旋的空间,所以只好引入间接层,让入参有机会宏展开。后面,很多宏函数都是这样实现,不得不间接调用,以便让宏全面展开。而msvc的宏展开机制更加奇葩,更加不人性化,其间接调用的形式也更丑陋。这都是没办法的事情。 #define PP_ASSERT() static_assert((__VA_ARGS__),PP_TEXT(__VA_ARGS__)); PP_ASSERT(...)里面的三个点,是不定参数的宏,而__VA_ARGS__就代表了...所匹配的所有参数,这条语法很重要,要熟练。这里,就不详细解释其用法了,后面会有大把大把的宏函数用到__VA_ARGS__。 #define PP_JOIN(_A,_B) _ZPP_JOIN_I(_A,_B) #define _ZPP_JOIN_I(_A,_B) _ZPP_JOIN_II(~,_A##_B) #define _ZPP_JOIN_II(p,res) res 竟然不止一层间接,而是两层,又多此一举,是因为发现在做宏递归的时候,一层间接调用还不能让宏充分地展开,所以只好又加间接层,也不明白是何原因,也懒得追究了。现在,接下来,当然是测试PP_JOIN了。各位同学,可以新建立一个测试文件,那个文件include我们的这个宏函数。当然,也可以在同一个文件里面写测试代码,注意分成两段代码,上一段写宏函数,下一段写测试代码,目前来看,都可以的,后面再整理。 PP_ASSERT(PP_JOIN(1+2,== 3)) #define A 20 #define B 10 PP_ASSERT(PP_JOIN(A + B,== 30)) 有了PP_JOIN,就可以开始做点其他事情了。比如,计数器, #define _ZPP_INC_JOIN(_A,_B) _ZPP_INC_JOIN_IMP1(_A,_B) #define _ZPP_INC_JOIN_IMP1(_A,_B) _ZPP_INC_JOIN_IMP2(~,_A##_B) #define _ZPP_INC_JOIN_IMP2(p,res) res #define PP_INC(x,) _ZPP_INC_JOIN(_ZPP_INC_,x) #define _ZPP_INC_0 1 #define _ZPP_INC_1 2 #define _ZPP_INC_2 3 #define _ZPP_INC_3 4 #define _ZPP_INC_4 5 #define _ZPP_INC_5 6 #define _ZPP_INC_6 7 #define _ZPP_INC_7 8 #define _ZPP_INC_8 9 #define _ZPP_INC_9 10 这里,我们重新又实现了一遍PP_JOIN,这也是没办法的事情,后面在重重嵌套的时候,会出现PP_JOIN里面又包含PP_JOIN的情况,这样会导致宏停止展开了,所以,只好对于每一个要用到JOIN之处,都用自己版本的JOIN。 #define PAIR_SECOND(x,y) y PP_ASSERT(PAIR_SECOND(10,20) == 20) 这样子,还不错,下面,再define一个宏函数,让其返回一个pair,也就是两个值 #define MAKE_PAIR(x,y) x,y (编辑:应用网_丽江站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |