zoukankan      html  css  js  c++  java
  • 使用宏实现日志信息以及异常处理

    背景:

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

  • 相关阅读:
    PNG文件格式具体解释
    opencv2对读书笔记——使用均值漂移算法查找物体
    Jackson的Json转换
    Java实现 蓝桥杯VIP 算法训练 装箱问题
    Java实现 蓝桥杯VIP 算法训练 装箱问题
    Java实现 蓝桥杯VIP 算法训练 单词接龙
    Java实现 蓝桥杯VIP 算法训练 单词接龙
    Java实现 蓝桥杯VIP 算法训练 方格取数
    Java实现 蓝桥杯VIP 算法训练 方格取数
    Java实现 蓝桥杯VIP 算法训练 单词接龙
  • 原文地址:https://www.cnblogs.com/zhaosc/p/3062860.html
Copyright © 2011-2022 走看看