可变参数

一、带有可变参数的宏

1. 方法一

在 1999 年版本的 ISO C 标准中,宏可以象函数一样,定义时可以带可变参数。宏的语法和函数的语法类似

1
#define debug(format, ...) fprintf(stderr, format, __VA_ARGS__)

这里的 ... 指可变参数。这类宏被表示成零个或多个符号,包括里面的逗号,一直到右括弧结束为止。

2. 方法二

GCC 可以给可变参数一个名字,如同其他参数一样。

1
#define debug(format, args...) fprintf(stderr, format, args)

3. 方法三

如上两种方式的定义有问题。就是可变参数要是没有的话,那么在编译期间就会报错。

C99 使用 ## 符号,如果可变参数被忽略或为空,## 操作将使预处理器去掉它前面的那个逗号。

1
#define debug(format, ...) fprintf(stderr, format, ##__VA_ARGS__)

二、使用宏定义封装的日志功能

使用宏定义封装的日志,可以很方便的切换不同的日志库,对应调试、线上发布很方便

1. 使用不定参数的宏定义

c99 规范,编译器就开始支持不定参数 ##__VA_ARGS__ 。如下所示,需要的时候打开宏定义开关,不需要关闭即可

1
2
3
4
5
6
7
#define __DEBUG

#ifdef __DEBUG
#define xxxlog(format, ...) printf("[%s:%d:%s] " format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#else
#define xxxlog(format, ...)
#endif

2. 分级日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#ifndef LOG_H_
#define LOG_H_

#define __LOG_SWITCH

// 定义日志模版
#ifdef __LOG_SWITCH
#define LOG_TEMPLATE(format, ...) printf(format, ##__VA_ARGS__)
#else
#define LOG_TEMPLATE(format, ...)
#endif

// 定义日志级别
enum LOG_LEVEL {
LOG_LEVEL_OFF = 0,
LOG_LEVEL_FATAL,
LOG_LEVEL_ERROR,
LOG_LEVEL_WARN,
LOG_LEVEL_INFO,
LOG_LEVEL_DEBUG
};

// 全局的日志级别,需要保证线程安全
enum LOG_LEVEL log_level = LOG_LEVEL::LOG_LEVEL_DEBUG;

#define LOG_FATAL(format, ...) \
do { \
if (log_level >= LOG_LEVEL_FATAL) \
LOG_TEMPLATE("[FATAL FILE:%s LINE:%d FUNC:%s] " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} while(0)

#define LOG_ERROR(format, ...) \
do { \
if (log_level >= LOG_LEVEL_ERROR) \
LOG_TEMPLATE("[ERROR FILE:%s LINE:%d FUNC:%s] " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} while(0)

#define LOG_WARN(format, ...) \
do { \
if (log_level >= LOG_LEVEL_WARN) \
LOG_TEMPLATE("[WARN FILE:%s LINE:%d FUNC:%s] " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} while(0)

#define LOG_INFO(format, ...) \
do { \
if (log_level >= LOG_LEVEL_INFO) \
LOG_TEMPLATE("[INFO FILE:%s LINE:%d FUNC:%s] " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} while(0)

#define LOG_DEBUG(format, ...) \
do { \
if (log_level >= LOG_LEVEL_DEBUG) \
LOG_TEMPLATE("[DEBUG FILE:%s LINE:%d FUNC:%s] " format "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
} while(0)

#endif // LOG_H_