30年前我念大学时从一个朋友那里学来的一个技巧。

它是汇编语言的一个宏,但很容易转换为C语言宏。

我一直在使用它,但有意思的是我还从没在别人的代码中看到过。现在该我把这个小技巧传递下去了。

让我们举个陈腐的栗子。假设我们有一个头文件叫color.h,里面有一个颜色的宏:

enum Color { Cred, Cblue, Cgreen };

在相应的源文件color.c中,为了正确的打印颜色,有一个字符串数组:

static char *ColorStrings[] = {"red", "blue", "green"};

我们可以这样使用:

enum Color c;
...
printf("the color is %s\n",
ColorStrings[c]);

到目前为止一切都很好。随着时间推移,假如新加入一个颜色:

enum Color{ Cred, Cyellow, Cblue, Cgreen };

是的,假如我们忘记更新数组ColorStrings[]了,打印Cyellow却输出了“blue”,更糟糕的是,如果打印Cgreen会造成数组越界。

(作为一个聪明的程序员,你是不可能犯这样的错误的,对么?)

主要问题是在enum和数组之间没有语义连接。 通常的解决办法是添加一个单元测试包。

但如果我们能找到一个连接enum和数组的方法,从而在编译时检测到此类错误,岂不美哉?

---  X 宏 ---

这是它的功能么?

它能做到这一点么?

X宏如下:

#define COLORS \
  X(Cred, "red") \
  X(Cblue, "blue") \
  X(Cgreen, "green")

把这个放在color.h中。接下来的是颜色枚举的定义:

#define X(a, b) a,
  enum Color { COLORS };
#undef X

在源代码文件color.c中这样定义数组:

#define X(a, b) b,
  static char *ColorStrings[] = { COLORS };
#undef X

可以看出,我们重新定义了X宏,以便提取出必要的信息而忽略其它。

正确的宏管理在这里得以体现,因为如果X已经定义过#define X将会抱怨,而#undef保证了这一点不会发生。

现在如果再添加一个颜色将变得非常简单:

#define COLORS \
  X(Cred, "red") \
  X(Cyellow, "yellow") \
  X(Cblue, "blue") \
  X(Cgreen, "green")

enum和数组都自动得到了更新,看起来很美妙是不是。有经验的程序员会立刻明白可以有更复杂的设计:

#define COLORS \
  X(red) \
  X(blue) \
  X(green) #define X(a) C##a,
  enum Color { COLORS };
#undef X #define X(a) #a,
  static char *ColorStrings[] = { COLORS };
#undef X

一个真实的例子是在C++编译器 Digital Mars 前端:

#define ENUMSCMAC \
  X(unde, SCEXP|SCKEP|SCSCT ) \
  X(auto, SCEXP|SCSS|SCRD ) \
  X(static, SCEXP|SCKEP|SCSCT) \
  X(thread, SCEXP|SCKEP ) \
  ...

3个独立但并行构造的构建 - 枚举,用于打印的字符串表,以及数组。

我使用过的最复杂的X宏有6个参数,它可以构造枚举,结构初始化,运行时初始化等。

当然,你可能已经在使用一个叫X的宏或者变量,且在宏内部X是硬编码的。

Andrei Alexandrescu(Author of Modern C++ Design)建议以下改进,即将X宏作为参数:

#define FOR_ALL_COLORS(apply) \
  apply(red) \
  apply(blue) \
  apply(green)

紧接着:

#define SELECT_STRING(a) #a,
static char *ColorStrings[] =
{
  FOR_ALL_COLORS(SELECT_STRING)
};
#undef SELECT_STRING

任何语言只要支持文本宏预处理程序,X宏技术就能大展身手。

C语言肯定能胜任工作。使用并且传播它,就像我贴心的朋友把他告诉我一样。:)

就像之前说的那样,我还从来没看见过其他人使用这个技巧。因为它晦涩难懂么?欢迎评论。

原文

扩展1

扩展2

编辑

X Macro的更多相关文章

  1. FreeMarker学习(宏<#macro>的使用)

    原文链接:https://my.oschina.net/weiweiblog/blog/506301?p=1 用户定义指令-使用@符合来调用  有两种不同的类型:Macro(宏)和transform( ...

  2. configure.ac:32: error: possibly undefined macro: AC_DEFINE

    在ubuntu 下编译snappy时,在检查依赖关系时,处理autoconf的包时,在相关依赖包都已经安装的情况下,报如下错误,死活不过. configure.ac:32: error: possib ...

  3. 【freemaker】之自定义指令<#macro>

    测试代码 @Test public void test07(){ try { root.put("name", "张三"); freemakerUtil.fpr ...

  4. C++ macro(宏)使用小结

    谈起C++中的宏,我们第一个想到的应该就是“#define”,它的基本语法长得像这样: #define macroname(para1, para2, para3, ... ,paran) macro ...

  5. Macro and SQL

    If you’ve developed anything in the supply chain area, you’ve most probably come across InventDimJoi ...

  6. The difference between macro and function I/Ofunction comparision(from c and pointer )

    macro is typeless and execute faster than funtion ,becaus of the overhead of calling and returnning ...

  7. Cmockery macro demo hacking

    /********************************************************************* * Cmockery macro demo hacking ...

  8. __KERNEL__ macro

    转载:http://blog.csdn.net/kasalyn/article/details/17097639 The __KERNEL__ macro is defined because the ...

  9. 幾種方法實現C語言Macro for debug

    1. #include <stdio.h> #include <stdlib.h> #define DEBUG 1 #ifdef DEBUG #define DEBUG_PRI ...

  10. jinja2 宏的简单使用总结(macro)

    Table of Contents 1. 简介 2. 用法 3. 参数和变量 4. 注意事项 4.1. macro的变量只能为如下三种: 4.2. 和block的关系: 5. 参考文档 1 简介 ji ...

随机推荐

  1. Stanford概率图模型: 第一讲 有向图-贝叶斯网络

    原文链接(系列):http://blog.csdn.net/yangliuy/article/details/8067261 概率图模型(Probabilistic Graphical Model)系 ...

  2. PhotoZoom如何使用调整大小配置文件

      众所周知PhotoZoom是一款图片无失真放大软件,所以在PhotoZoom中会有对尺寸大小调节功能.这里我们所要讲解的也是和尺寸调节等有关的功能——调整大小配置文件. 单击“调整大小配置文件.. ...

  3. Python3与2的故事一

    print函数:(Python3中print为一个函数,必须用括号括起来:Python2中print为class) Python 2 的 print 声明已经被 print() 函数取代了,这意味着我 ...

  4. bzoj 1121: [POI2008]激光发射器SZK 思维_结论

    Description 多边形相邻边垂直,边长为整数,边平行坐标轴.要在多边形的点上放一些激光发射器和接收器.满足下列要求: 1发射器和接收器不能放置在同一点: 2发射器发出激光可以沿壁反射,最终到达 ...

  5. Integer Intervals POJ - 1716_查分约束_

    Code: #include<cstdio> #include<queue> #include<algorithm> using namespace std; co ...

  6. 洛谷P3628 [APIO2010]特别行动队 斜率优化

    裸题,注意队列下标不要写错 Code: #include<cstdio> #include<algorithm> #include<cmath> using nam ...

  7. nginx视频服务缓存方案设置指导

    本文描述了如何通过设置nginx缓存达到降低服务器后端压力的效果以及结合nginx第三方插件ngx_cache_purge实现nginx缓存后的自动清理功能.具体实施步骤如下所示:第一步:获取清除清除 ...

  8. HDU2516 - 取石子游戏【斐波那契博弈】

    基本描述 有一堆个数为n的石子,游戏双方轮流取石子,满足: 先手不能再第一次把所有石子取完: 之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间,包括1和对手取的石子数的2倍.  取最后石子的人 ...

  9. MySQL数据库唯一性设置(unique index)

    1,命令行操作 分为两种.一种是在建表时就想好要加上唯一性,另一种是在后期才发现需要设置唯一性. 建表时: CREATE TABLE `t_user` ( `Id` int(11) NOT NULL ...

  10. bitset骚操作

    bitset的用途挺多的,是一个比较骚的常数优化 一.很多位数的二进制数 poj 2443 http://poj.org/problem?id=2443 直接开个1万位的二进制数,求交就行了. 有关集 ...