c++ 预处理之正整型实现方法

虽然通过一系列的奇技淫巧,让预处理也图灵完备一把,但是用预处理来做计算,真的很吃力不讨好。因为预处理一开始设计出来的目的,就没什么野心,原本就仅仅只是为了做简简单单的文本替换工作,并没有想过要成为正儿八经的编程语言,即便是最最缩水版脚本语言的功能要求都达不到。只是后来,实在是大量要求要批量自动生成代码,特别是c++11之前的版本玩什么模板元编程,铺天盖地的要有大量相似的代码。这些代码用其他工具来生成,当然形式会更加漂亮,但是始终还是用原生的预处理来做这种事情会更加的方便,否则每次修改,都要运行一遍外部工具,都麻烦啊!本人是倾向于用预处理来生成代码的。另外,c++11之后,的确原来很多需要宏来生成代码的场合已经不必要了,但是因为c++11的类型推导能力大大加强了之后,发现又有一大波地方可以用宏来生成代码了。并不是说C++中的宏是必不可少之物,但是用了宏,真的可以减少很多很多的重复代码,起码纸面上的代码清爽了很多。

预处理的原生数据类型就只有符号,然后符号只支持##的并接运算,同时,预处理也能识别并接后的结果(否则,并接运算就没意义了),如果是宏函数,就进行调用操作,如果是宏符号,就替换文本,如果什么都不是,就什么都不做,保留符号。但是这样的弱鸡类型,显然远远不能满足离经叛道的码猿需要。经过大量的宏编程的尝试之后,可以很肯定一点,预处理里面只能再模拟出来一种数据类型,那就是正整数,虽然通过补码运算来仿真负数,但是由于预处理里面的符号不能包含减号(-)字符,当然要花大力气捣鼓负整数也是可以的,只是使用上也不方便也不直观,性价比不高,基本上,必须用宏来生成代码的地方,都可以不需要负整数的。

另外,预处理也没有变量类型的概念,不要说强类型,就连弱类型也不是,完全就是无类型。正整数类型的概念全靠码猿人肉编译器来维护,一个循环的宏代码生成一般都是来来回回也不知道调用了多少层宏调用,任何一个地方出错,有时候是几吨密密麻麻的中间失败代码(编译器的预处理缓冲溢出,弃械投降),有时候就完全没有输出,没有任何一丁点的提示,简直是大海捞针的找问题。因此,在用宏循环生成代码时,必须小心翼翼,步步为营,不得不感慨,正儿八经语言里面的类型真是好东西啊。

其实,数据类型并不重要,重要的是数据上能够支持的运算集合以及这些运算能运用的场合。
好了,回到上文,我们用_ZPP_INC_N搞了10个数,通过复制粘贴,可以把N增加到255。实际运用中,完全足够用了。

#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
...
#define _ZPP_INC_255 256

同样的方式,再如法泡制PP_DEC,从256开始,一直递减到0为止。对于大于256的数,就不支持了,那就都是未定义操作。这样子,通过PP_INC(n),就得到n+1;而PP_DEC(n),则是n-1。比如PP_INC(PP_DEC(9)),其结果肯定是9了。很好,这样子,在预处理中就实现了自然数自增1和自减1的运算了。另外,对于大于256的数,比如512传递给PP_INC,就只得到一个_ZPP_INC_512的符号,完全没有任何意义。

然后,两个自然数是否相等的判断,也非常重要,必须支持。但是,在此之前,要实现一个宏函数PP_NOT,用来判断入参是否为0。为0的话,则函数返回1,否则,就返回0。也即是:
PP_NOT(0) == 1
PP_NOT(23) == 0,或者 PP_NOT(var) == 0。
记住,预处理提供给我们的原生类型就只有符号和##并接运算,除此之外,别无他物。好像工具太简陋,能完成目的吗?不得不佩服有些码猿的脑洞。以下代码是这样运作的,假设PP_NOT生成以下的调用形式,先不管PP_ARG1,至于符号~,是这样子的,可以看成普通的变量名字,它就是占位符,因为预处理只识别逗号(,),和小括号,至于其他符号,完全无视,那些是C/C++编译阶段才关心的符号。
PP_NOT(0) = PP_ARG1(~, 1, 0)
PP_NOT(n) = PP_ARG1(_ZPP_NOT_n, 0)
然后,让PP_ARG1取第二个参数(码猿的计数是从0开始的,也即是,0即是1,1即是2),就完成任务了。至于_ZPP_NOT_n是什么鬼,那个只是中间生成的临时符号,可以舍弃。我们只需对_ZPP_NOT_0做特别处理。因此,代码可以这样写了。PP_PROBE()用以生成两个入参
#define PP_PROBE() ~, 1
#define _ZPP_NOT_0 PP_PROBE()
#define PP_NOT(_X, ...) PP_IS(PP_JOIN(_ZPP_NOT_, _X))
# define PP_IS(...) PP_ARG1(__VA_ARGS__, 0)

这样子之后,显然PP_NOT(n)就可以变成PP_ARG1(_ZPP_NOT_n, 0)的形式了。PP_NOT不是只需一个入参吗?为何后面还要带省略号,纯粹是为了后面各种变态的运用,取悦编译器。已经用宏来写代码了,就不必再遵守什么清规戒律,只要能完成任务就行了。

至于PP_ARG1的实现,就很简单了,如下所示,
#define PP_ARG0(_0, ...) _0
#define PP_ARG1(_0, _1, ...) _1
#define PP_ARG2(_0, _1, _2, ...) _2

然后通过两次取反的函数,再补上函数PP_BOOL,如果入参>0,就返回1,否则返回0,类似于整型到bool的强制类型转换。
#define PP_BOOL(_X, ...) PP_NOT(PP_NOT(_X))

有了这些的铺垫之后,要比较两个自然数是否相等,就简单了。其实没什么神秘的,就是针对从0到255,重复256个以下形式的#define语句,
#define _ZPP_0_EQUALS_0 PP_PROBE()
#define _ZPP_1_EQUALS_1 PP_PROBE()
#define _ZPP_2_EQUALS_2 PP_PROBE()
...
#define PP_EQUALS(x, y) PP_IS(PP_CONCAT4(_ZPP_, x, _EQUALS_, y))
PP_EQUALS就是将入参并接成_ZPP_x_EQUALS_y的形式,只要x和y相同,也即是说,它们在上面的表格中,那么,道理就如同PP_NOT的实现那样,最后结果就是1了。其实,预处理中没有判断这种玩意,只有表格,只有并接,只有查表。所谓的图灵完备,说白了,没有玄虚的,就是建表,然后查表。对相等比较取反PP_NOT,自然就得到不相等的判断函数。
#define PP_UN_EQUALS(x, y) PP_NOT(PP_IS(PP_CONCAT4(_ZPP_, x, _EQUALS_, y)))
再次建表,就可以得到bool运算的函数,或与
#define PP_OR(a,b) PP_CONCAT3(_ZPP_OR_, a, b)
#define _ZPP_OR_00 0
#define _ZPP_OR_01 1
#define _ZPP_OR_10 1
#define _ZPP_OR_11 1

#define PP_AND(a,b) PP_CONCAT3(_ZPP_AND_, a, b)
#define _ZPP_AND_00 0
#define _ZPP_AND_01 0
#define _ZPP_AND_10 0
#define _ZPP_AND_11 1

再准备一张表格,将字节映射到8个二进制位。
#define _ZPP_BINARY_0 (0, 0, 0, 0, 0, 0, 0, 0)
#define _ZPP_BINARY_1 (0, 0, 0, 0, 0, 0, 0, 1)
#define _ZPP_BINARY_2 (0, 0, 0, 0, 0, 0, 1, 0)
#define _ZPP_BINARY_3 (0, 0, 0, 0, 0, 0, 1, 1)
#define _ZPP_BINARY_4 (0, 0, 0, 0, 0, 1, 0, 0)
...
然后通过模拟计算机组成原理里面的加减乘除的原理,就可以实现四则运算了。对了,整个预处理库的代码都在压缩包上,功能比boost的预处理库强多了,但是代码却少了很多,也容易理解多了,所有代码在vs下面正常运行,其他平台还没有测试。代码包:preprocessor.rar

(0)

相关推荐

  • c++ 预处理之正整型实现方法

    虽然通过一系列的奇技淫巧,让预处理也图灵完备一把,但是用预处理来做计算,真的很吃力不讨好.因为预处理一开始设计出来的目的,就没什么野心,原本就仅仅只是为了做简简单单的文本替换工作,并没有想过要成为正儿八经的编程语言,即便是最最缩水版脚本语言的功能要求都达不到.只是后来,实在是大量要求要批量自动生成代码,特别是c++11之前的版本玩什么模板元编程,铺天盖地的要有大量相似的代码.这些代码用其他工具来生成,当然形式会更加漂亮,但是始终还是用原生的预处理来做这种事情会更加的方便,否则每次修改,都要运行一

  • PHP不使用内置函数实现字符串转整型的方法示例

    介绍 php字符串类型的数字如果想转成整型的数字,一般我们都是采用系统内置的API去做转换,但如果规定就不让我们去用系统内置的API转换,而是让自己去实现一个函数转换该怎么办?这里我们看下如何去实现. 系统内置 API 方式 $num = '345432123'; //(一) $num = (int)$num; //输出: //int(345432123) //(二) $num = intval($num); //输出: //int(345432123) 采用 ASCII 码方式 下面我们利用

  • 在JavaScript中判断整型的N种方法示例介绍

    整数类型(Integer)在JavaScript经常会导致一些奇怪的问题.在ECMAScript的规范中,他们只存在于概念中: 所有的数字都是浮点数,并且整数只是没有一组没有小数的数字. 在这篇博客中,我会解释如何去检查某个值是否为整型. ECMAScript 5 在ES5中有很多方法你可以使用.有时侯,你可能想用自己的方法:一个isInteger(x)的函数,如果是整型返回true,否则返回false. 让我们看看一些例子. 通过余数检查 你可以使用余数运算(%),将一个数字按1求余,看看余数

  • Java基于高精度整型实现fibonacci数列的方法

    本文以实例形式讲述了Java基于高精度整型实现fibonacci数列的方法,分享给大家供大家参考之用.具体方法如下: package com.java.learning.recursion; import java.math.*; public class MainClass { public static void main(String args[]){ for(int i = 0; i < 100; i++){ f(i+1); } } public static BigInteger f(

  • 找到整型阵列中最大值和最小值的几种方法总结

    在整型阵列中,我们需要从中获取阵列元素的最大值和最小值: 方法一:先是使用Array进行排序,然后从排序后数组中,最一个元素为最小,最后一个元素为最大. Source Code public static int FindMaxNumber(params int[] stringValue) { Array.Sort(stringValue); return stringValue[stringValue.Length -1]; } public static int FindMinNumber

  • go实现整型的二进制转化的方法

    go中已经实现了int->bin的转化函数,我这里只是化过程逻辑的实现,至于原理我就假设大家都知道了 本案例只考虑 int->bin  的转化 包含了正整数,负整数,0 的转化 package main import ( "fmt" "strconv" ) //bin表示转化后的位数 func convertToBin(n int,bin int) string{ var b string switch { case n==0: for i:=0;i&l

  • PHP中把有符号整型转换为无符号整型方法

    在一个短地址项目中,根据六位字符与ID的相互映射的算法,当ID超过2147483647的时候,六位字符的短地址映射的ID变成有符号的整型. 复制代码 代码如下: ID        >     六位字符    >映射ID ID: 2147483644 > TfffVQ > 2147483644 ID: 2147483645 > efffVQ > 2147483645 ID: 2147483646 > NfffVQ > 2147483646 ID: 21474

  • 将IP地址转换为整型数字的PHP方法、Asp方法和MsSQL方法、MySQL方法

    首先我们要先了解一下IP地址转换为整型(严格来说应该说是长整型)的原理~ [转换原理]:假设IP为:w.x.y.z,则IP地址转为整型数字的计算公式为:intIP = 256*256*256*w + 256*256*x + 256*y + z [PHP的互转]:PHP的转换方式比较简单,它内置了两个函数int ip2long ( string $ip_address )和 string long2ip ( string $proper_address )可以直接调用使用~ [Asp的互转]:自定

  • 实例介绍Python中整型

    Python中有以下几个基本的数据类型: 整数 int 字符串 str 浮点数 float 集合 set 列表 list 元组 tuple 字典 dict 布尔类型 bool 日期 date 其中可变数据类型有:list(列表).dict(字典).set(集合): 不可变数据类型有:int(整型).float(浮点型).str(字符串).bool(布尔类型).tuple(元组): 什么可变对象及不可变对象? 不可变对象:就是说该对象所指向的内存中的值不能被改变.当改变某个变量时候,由于其所指的值

  • java中的取整与四舍五入方法实例

    一.java中取整数的方式 1.直接使用强制转换 public static void roundOne(){ System.out.println("正数:(int)10.12 = " + (int)10.12); System.out.println("负数:(int)-10.12 = " + (int)-10.12); System.out.println("---------------------------------"); Syst

随机推荐