在嵌入式软件,对打印级别控制非常有用, 如何实现对文件级进行控制呢?
通过宏扩展来进行高效灵活的控制。
实现方法
- 创建一个公共头文件
log.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #define LEVEL_NONE 0 #define LEVEL_ERROR 1 #define LEVEL_INFO 2
#ifdef THIS_FILE_LEVEL #define LEVEL THIS_FILE_LEVEL() #else #define LEVEL LEVEL_NONE #endif
#if LEVEL >= LEVEL_INFO #define log_info(arg...) print(arg) #else #define log_info(arg...) #endif #if LEVEL >= LEVEL_ERROR #define log_error(arg...) print(arg) #else #define log_error(arg...) #endif
|
- 在c文件中最开头定义该文件级别
1 2 3 4 5 6 7
| #define THIS_FILE_LEVEL() LEVEL_INFO #include "log.h"
void test() { log_info("Hello test"); }
|
通过这个方法,在不同的C文件开头定义不同的THIS_FILE_LEVEL()函数,就可以控制该文件的打印级
别了。
重点解释一下
这里为什么要将THIS_FILE_LEVEL定义为宏函数?如果定义为普通宏会怎么样?
定义为宏函数是为了延迟扩展,如果定义为普通函数,则在define时就完成扩展了,这时LEVEL_INFO还
没定义,就达不到想要的控制效果了。
品,细品。。。
扩展思考
通常情况下,一个文件属于一个Module,但一个Module可以有多个文件。如何通过Module级来进行控制
打印级别呢?
可以强制增加一个log_module.h来控制,在log.h开头包含该文件。考虑采用打印级别的优先级
THIS_FILE_LEVEL > THIS_MODULE_LEVEL
1 2 3 4 5 6 7 8 9 10
| #ifdef THIS_FILE_MODULE #define MODULE THIS_FILE_MODULE() #endif
#if defined A_MODULE && MODULE == A_MODULE #define THIS_MODULE_LEVEL() LEVEL_ERROR #elif defined B_MODULE && MODULE == B_MODULE #define THIS_MODULE_LEVEL() LEVEL_INFO #endif
|
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
| #include "log_module.h"
#define LEVEL_NONE 0 #define LEVEL_ERROR 1 #define LEVEL_INFO 2
#ifdef THIS_FILE_LEVEL #define LEVEL THIS_FILE_LEVEL() #elif defined THIS_MODULE_LEVEL #define LEVEL THIS_MODULE_LEVEL() #else #define LEVEL LEVEL_NONE #endif
#if LEVEL >= LEVEL_INFO #define log_info(arg...) print(arg) #else #define log_info(arg...) #endif #if LEVEL >= LEVEL_ERROR #define log_error(arg...) print(arg) #else #define log_error(arg...) #endif
|
1 2 3 4 5 6
| #define THIS_FILE_MODULE() A_MODULE #include "log.h"
...
|
参数的条件控制
有时候需要判断参数是否为空来决定是否要拼接打印。比如如下需求:
1
| #define EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(stat, msg...)
|
先执行语句stat, 当有msg需要输出时,拼接函数名同时输出msg,否则不输出任何信息。该实现
要求msg表达式不能以字符(开头, 一般也不会。参考
实现判断参数是否为空
当宏_IS_ARGS_EMPTY的参数args为空时,则扩展为1, 否则编译报错:
1 2
| #define _VALID_MACRO_FUNC(...) 1 define _IS_ARGS_EMPTY(args...) _VALID_MACRO_FUNC##args()
|
实现判断参数是否为空或以括号开头
在c语言中括号比较特殊,可以利用这个特性实现判断参数是否为空或以括号开头,如果是则扩展为1,
否则扩展为0. 如下IS_ARGS_EMPTY_OR_BWITH_BRACKET:
1 2 3 4 5 6 7 8 9 10
| #define _CONCAT2(a, b...) a##b #define CONCAT(a, b...) _CONCAT2(a, b) #define _GET_FIRST_MACRO_ARG(arg0, args...) arg0 #define _VALID_MACRO_FUNC_1 1, #define _VALID_MACRO_FUNC__VALID_MACRO_FUNC 0, #define _IS_ARGS_EMPTY_OR_BWITH_BRACKET_EXPAND(args...) \ _GET_FIRST_MACRO_ARG(args) #define IS_ARGS_EMPTY_OR_BWITH_BRACKET(args...) \ _IS_ARGS_EMPTY_OR_BWITH_BRACKET_EXPAND( \ CONCAT(_VALID_MACRO_FUNC_, _VALID_MACRO_FUNC args()))
|
注, CONCAT和_CONCAT2, IS_ARGS_EMPTY_OR_BWITH_BRACKET 和
_IS_ARGS_EMPTY_OR_BWITH_BRACKET_EXPAND 是利用宏的扩展机制而需要做的拆分。
实现Log控制
1 2 3 4 5 6 7 8
| #define _IS_ARGS_EMPTY_1(args...) _IS_ARGS_EMPTY(args) #define _IS_ARGS_EMPTY_0(args...) 0 #define IS_ARGS_EMPTY(args...) \ CONCAT(_IS_ARGS_EMPTY_, IS_ARGS_EMPTY_OR_BWITH_BRACKET(args))(args) #define _OUTPUT_LOG_2(arg0, args...) printf(arg0 "\n", ##args) #define _OUTPUT_LOG_0(args...) _OUTPUT_LOG_2("[log]" args) #define _OUTPUT_LOG_1(args...) #define OUTPUT_LOG(args...) CONCAT(_OUTPUT_LOG_, IS_ARGS_EMPTY(args))(args)
|
此时,通过OUTPUT_LOG就可以实现有Log时,加上头和尾后输出,无log时,不做任何动作。
实现命令和log结合
1 2 3 4 5 6
| #define EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(stat, msg...) \ do { \ stat; \ OUTPUT_LOG(msg); \ } while (0)
|
完整示例代码
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
| #include <stdio.h>
#define _VALID_MACRO_FUNC(...) 1 #define _IS_ARGS_EMPTY(args...) _VALID_MACRO_FUNC##args()
#define _CONCAT2(a, b...) a##b #define CONCAT(a, b...) _CONCAT2(a, b) #define _GET_FIRST_MACRO_ARG(arg0, args...) arg0 #define _VALID_MACRO_FUNC_1 1, #define _VALID_MACRO_FUNC__VALID_MACRO_FUNC 0, #define _IS_ARGS_EMPTY_OR_BWITH_BRACKET_EXPAND(args...) \ _GET_FIRST_MACRO_ARG(args) #define IS_ARGS_EMPTY_OR_BWITH_BRACKET(args...) \ _IS_ARGS_EMPTY_OR_BWITH_BRACKET_EXPAND( \ CONCAT(_VALID_MACRO_FUNC_, _VALID_MACRO_FUNC args()))
#define _IS_ARGS_EMPTY_1(args...) _IS_ARGS_EMPTY(args) #define _IS_ARGS_EMPTY_0(args...) 0 #define IS_ARGS_EMPTY(args...) \ CONCAT(_IS_ARGS_EMPTY_, IS_ARGS_EMPTY_OR_BWITH_BRACKET(args))(args) #define _OUTPUT_LOG_2(arg0, args...) printf(arg0 "\n", ##args) #define _OUTPUT_LOG_0(args...) _OUTPUT_LOG_2("[log]" args) #define _OUTPUT_LOG_1(args...) #define OUTPUT_LOG(args...) CONCAT(_OUTPUT_LOG_, IS_ARGS_EMPTY(args))(args)
#define EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(stat, msg...) \ do { \ stat; \ OUTPUT_LOG(msg); \ } while (0)
int main(void) { int i = 0; EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(i++, "%d", i); EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(i++, "hello"); EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(i++, "%d", i); EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(i++); EXECUTE_AND_PRINT_WITH_FUNC_IF_HAVE_MSG(i++, "%d", i); return 0; }
|