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"
",__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语言宏定义技巧

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

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

c语言的宏定义

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

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