背景:
在程序开发中难免会遇到一些错误,像java,C++中本身提供了一些捕获异常的机制,而C语言中并没有提供,这样C语言程序猿就要比较繁琐的处理这些问题。最近发现使用宏处理在C中的和日志记录和异常处理这样的公用模块,在每个C项目中都可以使用,感觉很方便,今天整理记录下。
我之前处理可能出现错误的方式:
1.调用一个函数;
2.如果返回一个错误,例如打开文件失败;
3.释放相关的资源;
4.打印错误的日志信息。
这样的处理意味着在调用每一个可能出现错误的函数后,我都要做相应的处理,相对比较繁琐。
解决方案:
使用宏定义来解决使用宏实现日志信息以及异常处理的问题,直接拿demo说话。
我先定义一个dbg.h头文件,这个头文件中定义了很多宏,来辅助我们高效的解决问题。
1 #ifndef __dbg_h__ 2 #define __dbg_h__ 3 4 #include <stdio.h> 5 #include <errno.h> 6 #include <string.h> 7 8 9 #define debug(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 10 11 #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 12 13 #define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) 14 15 #define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) 16 17 #define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__) 18 19 #define check(A, M, ...) if(!(A)) { log_err(M, ##__VA_ARGS__); errno=0; goto error; } 20 21 #define sentinel(M, ...) { log_err(M, ##__VA_ARGS__); errno=0; goto error; } 22 23 #define check_mem(A) check((A), "Out of memory.") 24 25 #define check_debug(A, M, ...) if(!(A)) { debug(M, ##__VA_ARGS__); errno=0; goto error; } 26 27 #endif
我的理解:
1.debug:,...与##__VA_ARGS__是对可变参数的支持,__FILE__是文件名称,__LINE__是当前行数。这样我们能够很方便的得知具体是哪个文件的哪块代码出现的问题;
2.clean_errno:根据错误编码,获得错误的信息,例如No such file or directory;
3.log_err,log_warn,log_info比较类似,通过1,2就比较容易理解啦;
4.check:检查A是否为true,如果不为true,把M和参数交给log_err打印,之后跳转的error,进行相关资源释放。对于M的理解看看一会的调用处就很好理解啦;
5.sentinel:在程序中不应该运行到的地方使用,打印出日志信息后,跳转到error。例如在if-else,swtich等分支语句中使用的情况比较多;
6.check_mem:这个就是针对于内存分配是否成功的检查;
用起来看看:
1 #include "dbg.h" 2 #include <stdlib.h> 3 #include <stdio.h> 4 5 6 void test_debug() 7 { 8 // notice you don't need the \n 9 debug("I have Brown Hair."); 10 11 // passing in arguments like printf 12 debug("I am %d years old.", 37); 13 } 14 15 void test_log_err() 16 { 17 log_err("I believe everything is broken."); 18 log_err("There are %d problems in %s.", 0, "space"); 19 } 20 21 void test_log_warn() 22 { 23 log_warn("You can safely ignore this."); 24 log_warn("Maybe consider looking at: %s.", "/etc/passwd"); 25 } 26 27 void test_log_info() 28 { 29 log_info("Well I did something mundane."); 30 log_info("It happened %f times today.", 1.3f); 31 } 32 33 int test_check(char *file_name) 34 { 35 FILE *input = NULL; 36 char *block = NULL; 37 38 block = malloc(100); 39 check_mem(block); // should work 40 41 input = fopen(file_name,"r"); 42 check(input, "Failed to open %s.", file_name); 43 44 free(block); 45 fclose(input); 46 return 0; 47 48 error: 49 if(block) free(block); 50 if(input) fclose(input); 51 return -1; 52 } 53 54 int test_sentinel(int code) 55 { 56 char *temp = malloc(100); 57 check_mem(temp); 58 59 switch(code) { 60 case 1: 61 log_info("It worked."); 62 break; 63 default: 64 sentinel("I shouldn't run."); 65 } 66 67 free(temp); 68 return 0; 69 70 error: 71 if(temp) free(temp); 72 return -1; 73 } 74 75 int test_check_mem() 76 { 77 char *test = NULL; 78 check_mem(test); 79 80 free(test); 81 return 1; 82 83 error: 84 return -1; 85 } 86 87 int test_check_debug() 88 { 89 int i = 0; 90 check_debug(i != 0, "Oops, I was 0."); 91 92 return 0; 93 error: 94 return -1; 95 } 96 97 int main(int argc, char *argv[]) 98 { 99 check(argc == 2, "Need an argument."); 100 101 test_debug(); 102 test_log_err(); 103 test_log_warn(); 104 test_log_info(); 105 106 check(test_check("ex20.c") == 0, "failed with ex20.c"); 107 check(test_check(argv[1]) == -1, "failed with argv"); 108 check(test_sentinel(1) == 0, "test_sentinel failed."); 109 check(test_sentinel(100) == -1, "test_sentinel failed."); 110 check(test_check_mem() == -1, "test_check_mem failed."); 111 check(test_check_debug() == -1, "test_check_debug failed."); 112 113 return 0; 114 115 error: 116 return 1; 117 }
仔细看一看这块代码,感觉今后遇到异常处理和信息记录的问题,就好办啦。
还不够完美:
上面的解决方案的确能够帮助我们处理很多问题,但是我想了想。当多个异常嵌套时,发生异常,我们怎么通过一个error去释放资源呢?这很明显是不合理的。怎么才能解决这个问题呢?我相信大家对宏定义有一定理解,会很快的解决的。
Note:如博文中存在问题,请大家及时指出,我会及时纠正,谢谢。