C语言宏技巧 X宏

Posted pusidun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;}


以上是关于C语言宏技巧 X宏的主要内容,如果未能解决你的问题,请参考以下文章

[转]C语言宏定义技巧

C语言陷阱与技巧第7节,define函数式宏定义不能用普通函数代替吗?为什么要使用do{}while包裹代码

C语言陷阱与技巧第18节,函数式宏定义的“缺陷”,没有参数类型检查,产生多次副作用怎么办?

c语言的宏定义

C语言带参数的宏定义:定义宏名时可以不写出参数???

C语言宏定义技巧——多次包括头文件内容不同