C错误检测宏

   学习UNIX编程已经有小几个星期了,从开始的什么都不懂,现在终于有了点感觉。

   看的是《高级UNIX编程》和《UNIX网络编程》这两本书。其实我最开始是想看《UNIX网络编程》来学习网络编程的,可是看着看着发现没有UNIX系统编程的知识真的不行。还好从学校借来了《高级UNIX编程》这本书,就都穿插着看了看。

 

   UNIX下的系统调用或者C标准函数有很多都会返回错误,所以对错误的处理很关键。

   最原始的就是将函数放在一个if语句中判断,然后在分别处理;可是用这种方式,代码中会有很多的if的语句(这些语句并非代码的主要功能逻辑,而只是为了可能发生的错误),而导致可读性的极具下降,同时每次都敲那么多代码非常麻烦。

   上面的两本书都有各自的错误处理方式。《UNIX网络编程》使用的是它叫作包裹函数的机制。自己定义包裹函数,由包裹函数调用实际函数、检查返回值,并在发生错误时终止进程。书上有个例子是这样的:

    int Socket(int family, int type, int protocol) { int n; if( (n = socket(family, type, protocol)) < 0) err_sys("socket_error"); return (n); }

在实际编程中再调用自己定义的包裹函数。用这样的方式,在编程中代码看起来会很简洁, 可是有个问题就是事先要定义巨大的包裹函数库,这是个长期积累的过程。

 

    再就是《高级UNIX编程》中对错误的处理是通过错误检测宏的机制。当我刚看到这个机制的时候,非常惊奇羡慕,只需要定义几个宏和几个函数,就可以应付几乎所有的错误检测,并且在代码中同样简介高效,可定制性也比之前的包裹函数要好。可是当我准备把那些宏抽取出来自己用的时候却发现了问题,那些函数的定义虽然不太复杂,可是却有调用其他地方的函数(貌似都是为了实现一个错误栈),调用得交错复杂的。如果想要照搬那些宏和函数要包含很多的头文件和定义,而且有些以我刚学的水平根本看不懂。

   还好我对错误检测的要求没有作者高,只需要能够检测和输出错误就行了,所以我对那些进行了大量的删减。将对错误栈的维护函数删除,改写了输出函数,基本上大的函数都被我删了。下面就是我删减的结果了:

ec.h:

#ifndef _EC_H_ #define _EC_H_ /* It might be nice to code functions or macros to replace the system calls or libraryfunctions (e.g., close_e()), but there's no way to execute a goto from an expression. So, ec_neg1(), etc., must be statements. Two alternatives: 1. Exit instead of goto, which is allowed in a function called within an expression. 2. In C++, use throw inside a function. #1 doesn't allow error recovery, and #2 doesn't work in C. */ #include #include #include #include #include typedef int bool; #define true 1 #define false 0 /*[basic]*/ extern bool ec_in_cleanup; #define EC_CLEANUP_BGN/ ec_warn();/ ec_cleanup_bgn:/ {/ bool ec_in_cleanup;/ ec_in_cleanup = true; #define EC_CLEANUP_END/ } #define ec_cmp(var, errrtn)/ {/ assert(!ec_in_cleanup);/ if ((int*)(var) == (int*)(errrtn)) {/ ec_print(__func__, __FILE__, __LINE__, #var, errrtn);/ goto ec_cleanup_bgn;/ }/ } #define ec_rv(var)/ {/ int errrtn;/ assert(!ec_in_cleanup);/ if ((errrtn = (var)) != 0) {/ ec_print(__func__, __FILE__, __LINE__, #var, errrtn);/ goto ec_cleanup_bgn;/ }/ } #define ec_ai(var)/ {/ int errrtn;/ assert(!ec_in_cleanup);/ if ((errrtn = (var)) != 0) {/ ec_print(__func__, __FILE__, __LINE__, #var, errrtn);/ goto ec_cleanup_bgn;/ }/ } #define ec_neg1(x) ec_cmp(x, -1) /* Not in book: 0 used instead of NULL to avoid warning from C++ compilers. */ #define ec_null(x) ec_cmp(x, 0) #define ec_zero(x) ec_null(x) /* not in book */ #define ec_false(x) ec_cmp(x, false) #define ec_eof(x) ec_cmp(x, EOF) #define ec_nzero(x)/ {/ if ((x) != 0)/ EC_FAIL/ } #define EC_FAIL ec_cmp(0, 0) #define EC_CLEANUP goto ec_cleanup_bgn; /*[]*/ #define EC_EINTERNAL INT_MAX void ec_print(const char *fcn, const char *file, int line, const char *str, int errno_arg); void ec_warn(void); #endif /* _EC_H_ */

函数实现,ec.c:

/* Error-checking support functions AUP2, Sec. 1.04.2 Copyright 2003 by Marc J. Rochkind. All rights reserved. May be copied only for purposes and under conditions described on the Web page www.basepath.com/aup/copyright.htm. The Example Files are provided "as is," without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. The author and his publisher are not responsible for any damages, direct or incidental, resulting from the use or non-use of these Example Files. The Example Files may contain defects, and some contain deliberate coding mistakes that were included for educational reasons. You are responsible for determining if and how the Example Files are to be used. */ #include "ec.h" #include bool ec_in_cleanup = false; static void ec_mutex(bool lock) { static pthread_mutex_t ec_mtx = PTHREAD_MUTEX_INITIALIZER; int errnum; char *msg; if (lock) { if ((errnum = pthread_mutex_lock(&ec_mtx)) == 0) return; } else { if ((errnum = pthread_mutex_unlock(&ec_mtx)) == 0) return; } if ((msg = strerror(errnum)) == NULL) fprintf(stderr, "Mutex error in ec_* function: %d/n", errnum); else fprintf(stderr, "Mutex error in ec_* function: %s/n", msg); exit(EXIT_FAILURE); } /*[ec_print]*/ void ec_print(const char *fcn, const char *file, int line, const char *str, int errno_arg) { ec_mutex(true); fprintf(stderr, "function %s in file %s at line %d ERROR: %m/n %s = %d/n", fcn, file, line, str, errno_arg); ec_mutex(false); } /*[ec_warn]*/ void ec_warn(void) { fprintf(stderr, "***WARNING: Control flowed into EC_CLEANUP_BGN/n"); } /*[]*/

 

   这里需要说明的是,作者源程序中,对错误的输出是将errno的值和相应的错误表达字符串建立了一个一一对应的关系,然后通过errno的值来查找输出。如下为一个映射关系:

{"errno", (intptr_t)EPERM, "EPERM", ""},

这样子处理麻烦而且,映射表的制作也很繁琐。刚好我前几天看到了可以通过:

fprintf(stderr, "%m/n"); 的形式输出错误,很好用。虽然还没有仔细探查内部的工作形式,但是很好用,所以我觉得可以替代那个表了,毕竟简洁

 

在使用上,只需要将ec.h和ec.c添加入自己的项目中,就行了(书上强大的检测宏要添加很多文件)。当然使用方法和书上的一样。下面是个例子:

/* * main.c * Author: Peter_ZHA * Created on: 2009-12-16 */ #include #include #include #include #include "ec.h" int main() { int fd; ec_neg1(fd = open("abc", O_RDONLY)) printf("error not occur!/n"); return 0; EC_CLEANUP_BGN printf("error occur!/n"); return 1; EC_CLEANUP_END }

因为文件"abc"不存在,所以发生错误,输出:

function main in file ../main.c at line 16 ERROR: No such file or directory fd = open("abc", 00) = -1 error occur!

 

  我是新手,如果有什么问题,请帮我指出来,谢谢;最后ec.c和ec.h的版权归属原书作者。

 

  欢迎转载,转载请著名出处

http://blog.csdn.net/zha_1525515/archive/2009/12/16/5019441.aspx

你可能感兴趣的:(linux,c,unix,编程,function,file,socket)