X Macro
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语言肯定能胜任工作。使用并且传播它,就像我贴心的朋友把他告诉我一样。:)
就像之前说的那样,我还从来没看见过其他人使用这个技巧。因为它晦涩难懂么?欢迎评论。
X Macro的更多相关文章
- FreeMarker学习(宏<#macro>的使用)
原文链接:https://my.oschina.net/weiweiblog/blog/506301?p=1 用户定义指令-使用@符合来调用 有两种不同的类型:Macro(宏)和transform( ...
- configure.ac:32: error: possibly undefined macro: AC_DEFINE
在ubuntu 下编译snappy时,在检查依赖关系时,处理autoconf的包时,在相关依赖包都已经安装的情况下,报如下错误,死活不过. configure.ac:32: error: possib ...
- 【freemaker】之自定义指令<#macro>
测试代码 @Test public void test07(){ try { root.put("name", "张三"); freemakerUtil.fpr ...
- C++ macro(宏)使用小结
谈起C++中的宏,我们第一个想到的应该就是“#define”,它的基本语法长得像这样: #define macroname(para1, para2, para3, ... ,paran) macro ...
- Macro and SQL
If you’ve developed anything in the supply chain area, you’ve most probably come across InventDimJoi ...
- 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 ...
- Cmockery macro demo hacking
/********************************************************************* * Cmockery macro demo hacking ...
- __KERNEL__ macro
转载:http://blog.csdn.net/kasalyn/article/details/17097639 The __KERNEL__ macro is defined because the ...
- 幾種方法實現C語言Macro for debug
1. #include <stdio.h> #include <stdlib.h> #define DEBUG 1 #ifdef DEBUG #define DEBUG_PRI ...
- jinja2 宏的简单使用总结(macro)
Table of Contents 1. 简介 2. 用法 3. 参数和变量 4. 注意事项 4.1. macro的变量只能为如下三种: 4.2. 和block的关系: 5. 参考文档 1 简介 ji ...
随机推荐
- boost多线程使用简例
原文链接:http://www.cppblog.com/toMyself/archive/2010/09/22/127347.html C++ Boost Thread 编程指南 转自cnblog: ...
- 应用二:Vue之ElementUI Form表单校验
(注:本文适用于有一定Vue基础或开发经验的读者,文章就知识点的讲解不一定全面,但却是开发过程中很实用的) 表单校验是前端开发过程中最常用到的功能之一,根据个人的工作经验总结在此对表单校验功能的基 ...
- poj 3669 bfs(这道题隐藏着一个大坑)
题意 在x,y坐标系,有流星会落下来,给出每颗流星落下来的坐标和时间,问你能否从(0,0)这个点到一个安全的位置.所谓的安全位置就是不会有流星落下的位置. 题解: 广搜,但是这里有一个深坑,就是搜索的 ...
- zabbix监控超详细搭建过程(转)
监控及zabbix 目录: 1 监控分类... 1 1.1 硬件监控... 1 1.2 系统监控... 2 1.3 网络监控... 3 1.4 ...
- CF140E New Year Garland (计数问题)
用$m$种颜色的彩球装点$n$层的圣诞树.圣诞树的第$i$层恰由$a_{i}$个彩球串成一行,且同一层内的相邻彩球颜色不同,同时相邻两层所使用彩球的颜色集合不 同.求有多少种装点方案,答案对$p$取模 ...
- flask_sqlalchemy和sqlalchemy联系区别及其使用方式
### 使用SQLAlchemy去连接数据库: 1.使用SQLALchemy去连接数据库,需要使用一些配置信息,然后将他们组合成满足条件的字符串:HOSTNAME = '127.0.0.1'PORT ...
- MAVEN项目的搭建
MAVEN能为我们做什么? 1.Jar的声明式依赖性管理 2.项目的自动构建 搭建流程 环境配置 http://maven.apache.org/download.html 下载最新版本Maven 3 ...
- nginx与tomcat搭建集群,负载均衡
--------------------------------------------------- 搭建环境(在桌面上即可完成测试) 先准备2个tomcat服务器 解压tomcat压缩包 得到 把 ...
- bzoj 1266 1266: [AHOI2006]上学路线route
1266: [AHOI2006]上学路线route Time Limit: 3 Sec Memory Limit: 162 MBSubmit: 2356 Solved: 841[Submit][S ...
- ASP.Net Cookie总结
Cookie是一段文本信息,在客户端存储 Cookie 是 ASP.NET 的会话状态将请求与会话关联的方法之一.Cookie 也可以直接用于在请求之间保持数据,但数据随后将存储在客户端并随每个请求一 ...