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. Programming Recipes

    Recipes是从一本书上看来的,即有诀窍又有食谱的意思.这里想记一些工作中遇到的问题和解决方法,说决窍有点过了,说食谱照单做又不足,所以Recipe这个词两个意思都有混合起来正合适. 1.Windo ...

  2. 优动漫PAINT(clip studio paint)怎么画一幅水墨竹子图

    今天小编分享使用优动漫PAINT绘制一个水墨竹子教程,绘画的过程中我只用到了两个笔刷,即钢笔模式下的“美术字”和“效果线专用”,并且全程鼠标绘制哦,所以生疏的笔触效果大家见谅,没有数位板的小伙伴不妨试 ...

  3. form表单提交三种方式,demo实例详解

    第一种:使用type=submit  可以直接提交 <html> <head> <title>submit直接提交</title> </head& ...

  4. WPF 创建用户控件并引用

    项目源码地址:https://github.com/lizhiqiang0204/WpfControlLibrary.git 首先创建新项目->WPF用户控件库项目 在UserControl1. ...

  5. React从安装到实战

    建议:初学者看之前请先看一遍菜鸟教程 可以安装一个ATOM编辑器,本人觉得很好用 一.安装并启动项目:网址  搭建好的项目目录为: 二.开始写项目: 1.组件到界面流程: 定义一个组件app.js导出 ...

  6. Javase 简单代码练习

    public class Test10 { public static void main(String[] args) { System.out.println("------------ ...

  7. 莫烦大大TensorFlow学习笔记(9)----可视化

      一.Matplotlib[结果可视化] #import os #os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import tensorflow as tf i ...

  8. python面向对象三大特性之一封装

    一.什么是封装 在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,其 含义是其他程序无法调用. 要了解封装,离不开“私有化”,就是将类或者 ...

  9. win10 1809磁盘占用总是100%

    快过年了,提前请假回家,装几台电脑公司备用.有个电脑装完系统开机很慢,开机完成之后电脑响应也很慢,于是打开任务管理器发现磁盘中用率一直是100%,然而程序读取数据的速度并不高. 解决思路: 关闭win ...

  10. 【codeforces 807A】Is it rated?

    [题目链接]:http://codeforces.com/contest/807/problem/A [题意] 给你n个人在一场CF前后的rating值; 问你这场比赛是不是计分的 [题解] 如果有一 ...