C语言宏技巧 X宏
Posted pusidun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言宏技巧 X宏相关的知识,希望对你有一定的参考价值。
前言
本文介绍下X宏的使用
首先简单介绍下宏的几种用法
/*
* 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等标准,方便运用新特性
编译器在进行源码编译的时候,会自动将这些宏替换为相应内容。
举个例子大家体会下
"%s %s [ERR]"M"\n",__FILE__,__LINE__,##__VA_ARGS__) define log_err(M,...) fprintf(stderr,
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的每个条件的写法是相似。既然格式相同,那么我们就可以简化:
case name: \
return
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) {
case name: \
return
break;
X(DEBUG)
X(INFO)
X(WARN)
X(ERROR)
default:
return "UNKNOW";
}
}
这样代码就紧凑多了。至于为什么这种宏叫X没考究过,可能是约定俗成的习惯吧。
最后留个思考,getLogLevel能不能也用X宏去实现?
提示:不用函数,直接用一个const char*[]数组去返回日志级别对应的字符串
答案参考如下:
X(DEBUG, ) \
X(INFO, ) \
X(WARN, ) \
X(ERROR, ) \
X(UNKNOW, )
enum LogLevel { mapLogLevel };
const char* strLogLevel[] = { mapLogLevel };
int main(){
LogLevel level=INFO;
cout<<strLogLevel[level]<<endl;
return 0;
}
以上是关于C语言宏技巧 X宏的主要内容,如果未能解决你的问题,请参考以下文章
C语言陷阱与技巧第7节,define函数式宏定义不能用普通函数代替吗?为什么要使用do{}while包裹代码