C语言宏技巧 X宏
前言
本文介绍下X宏的使用
首先简单介绍下宏的几种用法
#define STRCAT(X,Y) X##Y
#define _STR(X) #@X
#define STR(X) #X
#define Log(...) {printf(__VA_ARGS__);}
/*
* x##y 拼接xy
* #@x 单引号包裹'x'
* #x 字符串化,双引号包裹"x"
* __VA_ARGS__会扩展参数...
*/
ANSI C标准中有几个标准预定义宏(也是常用的):
LINE:在源代码中插入当前源代码行号;
FILE:在源文件中插入当前源文件名;
DATE:在源文件中插入当前的编译日期
TIME:在源文件中插入当前编译时间;
STDC:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
__cplusplus:当编写C++程序时该标识符被定义。常常被用来判断编译器是否符合C++11,C++14等标准,方便运用新特性
编译器在进行源码编译的时候,会自动将这些宏替换为相应内容。
举个例子大家体会下
#define log_err(M,...) fprintf(stderr,"%s %s [ERR]"M"\n",__FILE__,__LINE__,##__VA_ARGS__)
X宏
我们首先看一个日志系统的场景
enum LogLevel {DEBUG,INFO,WARN,ERROR,UNKNOW=-1};
string getLogLevel(LogLevel level) {
switch(level) {
case DEBUG:
return "DEBUG";
break;
case INFO:
return "INFO";
break;
case WARN:
return "WARN";
break;
case ERROR:
return "ERROR";
break;
default:
return "UNKNOW";
}
}
我们定义了一个结构体,表示日志级别。然后定义了一个函数,返回一个字符串,用于获取日志级别对应的名称。
例子比较简单,但我们发现,这个代码有“重复”的部分。case的每个条件的写法是相似。既然格式相同,那么我们就可以简化:
#define X(name) \
case name: \
return #name; \
break;
string getLogLevel(LogLevel level) {
switch(level) {
X(DEBUG)
X(INFO)
X(WARN)
X(ERROR)
default:
return "UNKNOW";
}
}
前面也说过了,#name是字符串化,name如果是INFO的话,#name就会转化成"INFO"
看上去代码已经清爽多了。为避免其他地方也用到X宏,我们可以在使用完毕立刻undef掉,宏就写在函数内,使代码更加紧凑,改进后如下:
string getLogLevel(LogLevel level) {
switch(level) {
#define X(name) \
case name: \
return #name; \
break;
X(DEBUG)
X(INFO)
X(WARN)
X(ERROR)
#undef X
default:
return "UNKNOW";
}
}
这样代码就紧凑多了。至于为什么这种宏叫X没考究过,可能是约定俗成的习惯吧。
最后留个思考,getLogLevel能不能也用X宏去实现?
提示:不用函数,直接用一个const char*[]数组去返回日志级别对应的字符串
答案参考如下:
#define mapLogLevel \
X(DEBUG, "DEBUG") \
X(INFO, "INFO") \
X(WARN, "WARN") \
X(ERROR, "ERROR") \
X(UNKNOW, "UNKNOW")
#define X(a, b) a,
enum LogLevel { mapLogLevel };
#undef X
#define X(a, b) b,
const char* strLogLevel[] = { mapLogLevel };
#undef X
int main(){
LogLevel level=INFO;
cout<<strLogLevel[level]<<endl;
return 0;
}
本文章同时更新微信公众号pusidun,欢迎关注

C语言宏技巧 X宏的更多相关文章
- C奇淫技巧,——宏神奇
一个.宏列表 当这个问题面临: 有一个标志变量.位代表对应的含义. 我们须要提供一组函数来訪问设置这些位.可是对于每一个标记位的操作函数都是相似的.若有32个位,难道要搞32套相似的操作函数么? 你或 ...
- Linux C编程学习之C语言简介---预处理、宏、文件包含……
C的简介 C语言的结构极其紧凑,C语言是一种模块化的编程语言,整个程序可以分割为几个相对独立的功能模块,模块之间的相互调用和数据传递是非常方便的 C语言的表达能力十分强大.C语言兼顾了高级语言和汇编语 ...
- C语言 预处理二(宏定义--#define)
//#define 宏定义(宏定义一般大写) //知识点一-->#define的作用域:从#define开始,从上往下,如果遇到#undef就到#undef处结束,如果没有就是作用于当前整个文件 ...
- C语言宏定义和宏定义函数
要写好C语言,漂亮的宏定义是非常重要的.宏定义可以帮助我们防止出错,提高代码的可移植性和可读性等. 在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定 ...
- 一起talk C栗子吧(第一百二十四回:C语言实例--内置宏)
各位看官们,大家好,上一回中咱们说的是显示变量和函数地址的样例,这一回咱们说的样例是:内置宏.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,我们在编译程序的时候,假设有语法错误,编译器就 ...
- C语言面试题分类->宏定义
1.写一个“标准”宏,这个宏输入两个参数并返回较小的一个 答:#define MIN(x, y) ((x)<(y)?(x):(y))//注意x,y要加括号,因为x,y如果有复合运算会出现问题. ...
- C语言学习笔记 函数式宏
不学C光搞PHP不知道还有这种东西-函数式宏,宏前面学过了Macro,编译器在对代码进行编译时会对宏表达式进行展开替换,这样宏就起到了全局变量的作用,这里函数式宏也是类似,编译器进行编译时按函数表达是 ...
- C 语言常用方法技巧
C语言常用方法技巧 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impor ...
- Excel VBA(宏):添加宏
写在前面: .编写宏,打开VBA,双击ThisWorkbook对当前工作薄进行编写宏:双击Sheet1,对整个sheet编写宏: 或者创建模块,在模块里,编写.调试代码. 打开VBA的方法见第一讲,结 ...
随机推荐
- Docker安装常见的应用与将本地镜像推送到阿里云
一.Docker安装常用的应用 1,docker安装mysql #拉取镜像mysql5.7 docker pull mysql:5.7 #启动容器(绑定对应的配置文件和日志,默认密码为123456) ...
- python之robotframework+pycharm测试框架
一.robotframework简介 Robot Framework是一款python编写的功能自动化测试框架.具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型的客户端或者接口,可以进行分布式 ...
- 二维DCT变换 | Python实现
引言 最近专业课在学信息隐藏与数字水印,上到了变换域隐藏技术,提到了其中的DCT变换,遂布置了一个巨烦人的作业,让手动给两个\(8\times8\)的矩阵做二维DCT变换,在苦逼的算了一小时后,我决定 ...
- 50个SQL语句(MySQL版) 问题四
--------------------------表结构-------------------------- student(StuId,StuName,StuAge,StuSex) 学生表 tea ...
- Chisel3 - bind - Op, ReadOnly, 左值
https://mp.weixin.qq.com/s/F_08jKFMoX9Gf_J_YpsDpg 两个数据变量进行某个操作(op),产生一个输出,这个输出存在一个匿名变量中.这个匿名变量就是以O ...
- MethodHandle(方法句柄)系列之三:invoke和invokeExact的区别
先把代码贴上来,用的是一样的代码 /** * * @author LiuYeFeng<897908343@qq.com> * @date 2015年4月8日 下午10:41:13 * @C ...
- SpringMVC(一)概述、解析器与注解
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.SpringMVC的概述 1.概述 Spring MVC框架是一个开源的Java平台,为开发强大的基 ...
- Java实现 LeetCode 743 网络延迟时间(Dijkstra经典例题)
743. 网络延迟时间 有 N 个网络节点,标记为 1 到 N. 给定一个列表 times,表示信号经过有向边的传递时间. times[i] = (u, v, w),其中 u 是源节点,v 是目标节点 ...
- Java实现 LeetCode 698 划分为k个相等的子集(递归)
698. 划分为k个相等的子集 给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等. 示例 1: 输入: nums = [4, 3, 2, 3, ...
- Java实现 蓝桥杯VIP 算法提高 质数的后代
算法提高 质数的后代 时间限制:1.0s 内存限制:256.0MB 问题描述 在上一季里,曾提到过质数的孤独,其实从另一个角度看,无情隔膜它们的合数全是质数的后代,因为合数可以由质数相乘结合而得. 如 ...