宏定义黑魔法

 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  XXX
    LOG_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_FIELDS
    uint32_t    timeout;                \
};

struct hperiod_s {
    HTIMER_FIELDS
    int8_t      minute;
    int8_t      hour;
    int8_t      day;
    int8_t      week;
    int8_t      month;
};

htimer_s、htimeout_s和hperiod_s都包含HTIMER_FIELDS,就相当于这三个结构体“继承”了同一个“基类”,拥有共同的一些成员。但是我个人不喜欢这种方式,我宁愿将该“基类”实现为一个结构体,然后作为其他三个结构体的内部成员。我觉得使用”基类结构体“的方式比使用宏更具意义,也更可读。

先写这么多吧。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值