这两天稍稍看了一下boost的preprocessor库,发觉boost那帮疯子竟然利用各种奇技淫巧定义出各种数据类型和结构,还在上面定义出加减乘除等等各种运算,在快速浏览的过程中,还瞄到了很眼熟的各种宏名:list,cons,fold_left、fold_right,估计这帮人把函数式语言的很多特性也搬上去。
 
本着造轮子练本领的原则,我也尝试自己去实现各种元素,可是智商不够,越写越难受,最后无疾而终。
 
大致总结了一下,暂时发现C的宏有以下反直觉的缺点:
 
1、无法定义局部变量,所有宏必须在最外层定义,致使全局可见
     而且没有类似namespace的功能,命名时超头疼
 
    不支持多行出写,若要多行需在每行末端加 "\"
 
2、无控制流,要实现循环、选择非常麻烦
 
3、传参机制反直觉,正常语言的传参一般采用应用序,先完全展开参数再传入,而C的宏参数展开过程中若遇到#或##就停止展开,如:
       
      #define BOOL(n)      BOOL##n
#define BOOL0 0
#define BOOL1 1
#define BOOL2 1
#define BOOL3 1
    BOOL(n)可获取值n的真假值
 
 #define IF(c, x, y)       IF##c(x, y)
#define IF0(x, y) y
#define IF1(x, y) x

上面的宏是想要实现选择控制,IF中传入逻辑值c,若c为0则返回y, 若c为1则返回x

    假如按如下调用:  IF( BOOL(3),  "t", "f" ),按直觉此句应生成“t”,
    可事与愿违,   因为展开BOOL(3)时碰到##,所以直接返回BOOL3,
    结果上面的宏就变成了 IF( BOOL3,  "t", "f"),按IF宏体继续展开, 则变成了 IFBOOL3("t", "f")
    最后预处理器报错: 找不到IFBOOL3
 
    因此,为了能正确地把参数BOOL(3)展开为1,还需要多包装多一层宏:
    
 #define IF(c, x, y)       IF_C(c, x, y)
#define IF_C(c, x, y) IF##c(x, y)

这样,IF( BOOL(3),  "t", "f" )就会先展开参数,变成 IF( BOOL3,  "t", "f"),

    然后宏体展开, IF_C( BOOL3, "t", "f" ),展开参数成了, IF_C( 1, "t", "f" ),
    最后才会正确地展开成 IF1( "t", "f" )
 
4、缺少整数类型,若要利用计数器循环生成代码时非常麻烦,首先要自己手工定义一堆整数的INC:
       
       #define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
…………… #define DEC_x x
………………
 
        然后再在INC_xx和DEC_xx之上定义加法,减法,
        这样做相当于需要手工利用最基本的元素构造基本方法,再将这些基本方法不停地复合嵌套,抽象出更高阶的函数,
        工作量跟创造语言差不多
 
        本来创造语言还是挺有趣的一件事,可由于刚刚提过的反人类反直觉的古怪传参机制的存在,
        致使复合方法构造高阶函数的过程异常痛苦,得不时留意参数展开时会不会被#和##打断,若被打断则需要增加一层宏来继续展开。
 
5、无法实现递归,如:
       
    #define x y+1
#define y x+1

则展开x时,先展开成y+1,继续展开y,x+1+1,这时又碰到了x,预处理器便停止展开了。

 
    无法实现递归,那利用宏实现循环时就变得异常冗长了。
    一般来说,while循环和尾递归是等价的,所以若支持递归,则可用尾递归的形式实现循环,但现在不支持,
    所以我们需要把尾递归的每一步都得亲自展开,并将其手工显示的定义成宏,如:
 

         #define WHLE(...)       WHILE##n(...)
#define WHILE0(...) xxxxx
#define WHLE1(....) WHILE0(......)
#define WHLE2(....) WHILE1(......)
#define WHLE3(....) WHILE2(......)
...................
     这样做不仅麻烦,而且递归深度也只能是一个固定值
 
6、c的宏只是作简单的文本替换,所以替换到文本后可能会出现,下面就是一个最经典的例子:
 

       #define square(x)  x*x
cout<<square(+)<<endl;

替换后就变成了2+3*2+3,所以写宏时还要注意在必要的地方加括号。。。。。。。。。

 
7、没办法传function-like macro的名字,如:
 
         #define ADD(n, m)   ..........
#define FOR(k, op,...) ......

若调用FOR((3,3), ADD,...),想要在FOR内部ADD(3,3),发现预处理器会报错,说ADD没定义。

      也就是说函数名不能当参数传入,当然我发现boost里面是可以的,估计是用了什么奇技淫巧,没耐性看,各位大神知道的话请指点以下。

C宏系统缺陷的更多相关文章

  1. 【2018.10.11 C与C++基础】C Preprocessor的功能及缺陷(草稿)

    一.前言及参考资料 C Preprocessor即所谓的C预处理器,C++也继承了C的预处理程序,但在C++语言的设计与演化一书中,C++的设计者Bjarne Strustrup提及他从未喜欢过C预处 ...

  2. ES6笔记(一):ES6所改良的javascript“缺陷”

    块级作用域 ES5没有块级作用域,只有全局作用域和函数作用域,由于这一点,变量的作用域甚广,所以一进入函数就要马上将它创建出来.这就造成了所谓的变量提升. ES5的"变量提升"这一 ...

  3. B/S系统常见缺陷整理和解决方案

    最近部门整理了今年所有项目测试团队提出的BUG,筛选了几十个作为常规通用的缺陷,我根据这些缺陷内容,去掉和业务相关的知识,整理出了一份缺陷描述和解决方案. 其实WEB系统中常规的缺陷分类后也就那么多, ...

  4. 阮一峰对js的见解(10大缺陷)

    一.为什么Javascript有设计缺陷?这里有三个客观原因,导致Javascript的设计不够完善.1. 设计阶段过于仓促Javascript的设计,其实只用了十天.而且,设计师是为了向公司交差,本 ...

  5. iOS UIPageViewController缺陷

    为什么弃用UIPageViewController?问题1:设置UIPageViewController为UIPageViewControllerTransitionStyleScroll且调用set ...

  6. PHP处理0e开头md5哈希字符串缺陷/bug

    PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他 ...

  7. bookstore网上书店测试缺陷报告2

    Bookstore网上书店系统测试缺陷报告   缺陷编号 01.01.0002 发现人 吴赵昕 记录日期 2016-06-10 所属模块 购物车 确认人 吴赵昕 确认日期 2016-06-10 当前状 ...

  8. bookstores网上书店测试缺陷报告1

    Bookstore网上书店系统测试缺陷报告   缺陷编号 01.01.0001 发现人 吴赵昕 记录日期 2016-06-10 所属模块 购物车 确认人 吴赵昕 确认日期 2016-06-10 当前状 ...

  9. HoloLens外包团队:HoloLens有一个严重缺陷,但微软并不想改

    微软的HoloLens自公布以来,几乎获得了一边倒的赞美声.它使用了增强现实(AR)技术,在现实世界中叠加一层虚拟影像,能让人仿佛置身魔法世界.但从最新的Hololens硬件体验上看,它可能有一个致使 ...

随机推荐

  1. 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)

    命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...

  2. JavaScript XML 兼容处理,序列化和反序列化以及回调事件

    浏览器中XML DOM的支持 IE中通过ActiveXObject实现了XML的支持,存在一下几个版本:Microsoft.XmlDom,MSXML2.DOMDocument,MSXML2.DOMDo ...

  3. A Dicey Problem 骰子难题(Uva 810)

    题目描述:https://uva.onlinejudge.org/external/8/810.pdf 把一个骰子放在一个M x N的地图上,让他按照规定滚动,求滚回原点的最短路径. 思路:  记忆化 ...

  4. cobbler常见问题

    http://@@http_server@@/cblr/links/CentOS-6.4-x86_64 cobbler cblr/svc 四.配置文件 cobbler有许多的配置文件,但是只有少部分基 ...

  5. python 多级菜单 纯循环与分支

    源代码: dic1 = {'湖南':{'衡阳':{'珠晖区':'湖南工学院'},'湘潭':{'晖晖':'啦啦'}}, '北京':{'朝阳': {"德玛:北京大学"}}}print( ...

  6. 转换 TColor 到 HTML 颜色串

      //转换 TColor 到 HTML 颜色串 function ColorToHtml(color:TColor):string; var RgbColor : TColorRef; begin ...

  7. 写在新建博客的第一天 分类: fool_tree的笔记本 2014-11-08 17:57 144人阅读 评论(0) 收藏

    来CSDN开博客的目的有两个: 其一是因为CSDN的代码输出,看过一些博文,觉得这里的代码输出真的很漂亮: 其二则是因为,感觉自己印象笔记用久了之后,渐渐地几乎不再自己写些东西了,习惯了方便的剪藏插件 ...

  8. js获取键盘的keyCode-------Day42

    济南今天是大雨倾盆啊,这闷热一扫而空,只是有些电闪雷鸣的,原想在公司里就完毕今天的博客记录的,只是不知道为什么怎么也登不上博客,预计是CSDN当时的server出问题了吧,好在到了晚上,这雷声小了也少 ...

  9. shuffle() 函数(转)

    定义和用法 shuffle() 函数把数组中的元素按随机顺序重新排列. 若成功,则返回 TRUE,否则返回 FALSE. 注释:本函数为数组中的单元赋予新的键名.这将删除原有的键名而不仅是重新排序. ...

  10. Qt 学习之路:QStringListModel

    上一章我们已经了解到有关 list.table 和 tree 三个最常用的视图类的便捷类的使用.前面也提到过,由于这些类仅仅是提供方便,功能.实现自然不如真正的 model/view 强大.从本章起, ...