背景:

  在程序开发中难免会遇到一些错误,像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:如博文中存在问题,请大家及时指出,我会及时纠正,谢谢。

 

posted on 2013-05-07 10:22  cczscq  阅读(470)  评论(0编辑  收藏  举报