定义黑魔法"/>
宏定义黑魔法
1、
#define CLR_RED "\033[31m" /* 红色字 */
#define CLR_GREEN "\033[32m" /* 绿色字 */
#define CLR_YELLOW "\033[33m" /* 黄色字 */
#define CLR_BLUE "\033[34m" /* 蓝色字 */
#define CLR_PURPLE "\033[35m" /* 紫色字 */
#define CLR_SKYBLUE "\033[36m" /* 天蓝字 */
#define CLR_WHITE "\033[37m" /* 白色字 */#define CLR_BLK_WHT "\033[40;37m" /* 黑底白字 */
#define CLR_RED_WHT "\033[41;37m" /* 红底白字 */#define LOG_LEVEL_MAP(XXX) \XXX(LOG_LEVEL_DEBUG, "DEBUG", CLR_WHITE) \XXX(LOG_LEVEL_INFO, "INFO ", CLR_GREEN) \XXX(LOG_LEVEL_WARN, "WARN ", CLR_YELLOW) \XXX(LOG_LEVEL_ERROR, "ERROR", CLR_RED) \XXX(LOG_LEVEL_FATAL, "FATAL", CLR_RED_WHT)typedef enum {LOG_LEVEL_VERBOSE = 0,
#define XXX(id, str, clr) id,LOG_LEVEL_MAP(XXX)
#undef XXXLOG_LEVEL_SILENT
} log_level_e;
这个宏的作用是在日志中,对不同级别的日志,选择不同的颜色打印。上面的是表示颜色的宏,很简单。 关键是下面的LOG_LEVEL_MAP,这个宏可以说是一个二级宏,不知道有没有这个概念,因为它后面括号里的内容,也是一个宏。看下面的用法,在LOG_LEVEL_MAP宏的下面定义了一个枚举,这个枚举表示告警的级别。在这个枚举的中,定义了另一个宏。先把LOG_LEVEL_MAP(XXX)的第一层宏展开,是这样的:
typedef enum {LOG_LEVEL_VERBOSE = 0,XXX(LOG_LEVEL_DEBUG, "DEBUG", CLR_WHITE) \XXX(LOG_LEVEL_INFO, "INFO ", CLR_GREEN) \XXX(LOG_LEVEL_WARN, "WARN ", CLR_YELLOW) \XXX(LOG_LEVEL_ERROR, "ERROR", CLR_RED) \XXX(LOG_LEVEL_FATAL, "FATAL", CLR_RED_WHT)LOG_LEVEL_SILENT
} log_level_e;
这里为了看起来方便,把里面的宏定义取掉了。展开第一层后,XXX还是一个宏,在enum中定义的该宏,所以需要继续展开,根据第一段代码中的宏定义,展开后是这样的:
typedef enum {LOG_LEVEL_VERBOSE = 0,LOG_LEVEL_DEBUG,LOG_LEVEL_INFO,LOG_LEVEL_WARN,LOG_LEVEL_ERROR,LOG_LEVEL_FATAL, LOG_LEVEL_SILENT
} log_level_e;
这样看起来就简单多了,但这还不是全部。。。
第二个使用该宏的地方:
const char* pcolor = "";const char* plevel = "";
#define XXX(id, str, clr) \case id: plevel = str; pcolor = clr; break;switch (level) {LOG_LEVEL_MAP(XXX)}
#undef XXX
第一层展开后跟上面的是一样的,主要就是第二层,这里直接展开第二层:
switch (level) {case LOG_LEVEL_DEBUG: plevel = "DEBUG", pcolor = CLR_WHITE; break;case LOG_LEVEL_INFO: plevel = "INFO", pcolor = CLR_GREEN; break;case LOG_LEVEL_WARN: plevel = "WARN", pcolor = CLR_YELLOW; break;case LOG_LEVEL_ERROR: plevel = "ERROR", pcolor = CLR_RED; break;case LOG_LEVEL_FATAL: plevel = "FATAL", pcolor = CLR_RED_WHT; break;}
是不是很简单了,就是根据日志级别,设置相应的信息。当然,上面所有的宏展开实际上都在一行,只不过我为了看起来简单,分了行。
2、使用宏实现类似于C++中容器的功能
在array.h和queue.h中,使用宏定义实现了类似于vector和deque的功能。因为这两个功能的实现方式差不多,而且理解起来很简单,就不贴接口了,只看看结构体。
#define ARRAY_DECL(type, atype) \
struct atype { \type* ptr; \size_t size; \size_t maxsize;\
}; \
typedef struct atype atype;\
type就是容器中元素的类型,atype就是这个容器的类型。举个程序中使用例子
ARRAY_DECL(struct epoll_event, events)
这样就定义了一个容器,容器类型为events,成员类型为struct epoll_event,展开就是
struct events { struct epoll_event* ptr; size_t size; size_t maxsize;
};
功能挺强大,但理解起来挺简单。当然了,比起C++标准库的容器,用起来肯定还是麻烦一些。
3、使用宏实现类似C++继承的功能
#define HTIMER_FIELDS \HEVENT_FIELDS \uint32_t repeat; \uint64_t next_timeout; \struct heap_node node;struct htimer_s {HTIMER_FIELDS
};struct htimeout_s {HTIMER_FIELDSuint32_t timeout; \
};struct hperiod_s {HTIMER_FIELDSint8_t minute;int8_t hour;int8_t day;int8_t week;int8_t month;
};
htimer_s、htimeout_s和hperiod_s都包含HTIMER_FIELDS,就相当于这三个结构体“继承”了同一个“基类”,拥有共同的一些成员。但是我个人不喜欢这种方式,我宁愿将该“基类”实现为一个结构体,然后作为其他三个结构体的内部成员。我觉得使用”基类结构体“的方式比使用宏更具意义,也更可读。
先写这么多吧。。。
更多推荐
宏定义黑魔法
发布评论