• 使用宏实现日志信息以及异常处理


    背景:

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

  • 相关阅读:
    响应式开发: 宽高等比例缩放
    node服务成长之路
    node压力测试
    前端开发工具
    sequelize问题集锦
    webpack引入handlebars报错'You must pass a string or Handlebars AST to Handlebars.compile'
    夏夜无题
    jmeter在windows环境下系统参数设置
    服务端性能优化指南
    修车备忘
  • 原文地址:https://www.cnblogs.com/zhaosc/p/3062860.html
Copyright © 2020-2023  润新知