原创作者: xombat   阅读:12102次   评论:2条   更新时间:2011-05-26    

引子:


不管是在c++还是在java中,异常都被认为是一种很优雅的处理错误的机制,而如果想在c语言中使用异常就比较麻烦。但是我们仍然可以使用c语言中强大的setjmp和longjmp函数实现类似于c++的异常处理机制。


有关c语言中setjmp和longjmp的资料可以参考:
C语言中一种更优雅的异常处理机制:http://blog.csdn.net/hello_wyq/archive/2006/06/23/826312.aspx
全面了解setjmp与longjmp的使用:http://blog.csdn.net/hello_wyq/archive/2006/06/16/804040.aspx


基本原理


结合setjmp,将当前的环境变量打包为frame(定义的一个结构名)压到一个异常堆栈(自定义的结构体)中,如果程序段正常运行,将此frame弹出,而如果程序出错,将异常栈的顶部元素弹出,根据这个栈顶元素的frame中保存的环境变量,通过setjmp将环境恢复,然后执行某个错误处理函数,而如果没有相应错误处理函数,重新弹出新的栈顶元素,以跳到更外层的setjmp块进行处理。


主要代码分析


此异常机制的实现大量应用了宏,以实现c++和java中异常处理的语法效果。如何使用见下面的如何使用部分。
try部分,作用见注释:
c 代码

  1. //try的作用就是将一个包含环境变量env的except_frame压入Except_stack栈中。   
  2. //其中except_flag为setjmp的返回值,表示异常的状态    
  3. #define try do{ \   
  4.             volatile int except_flag; \   
  5.             Except_frame except_frame; \   
  6.             except_frame.prev = Except_stack; \   
  7.             Except_stack = &except_frame; \   
  8.             except_flag = setjmp(except_frame.env); \   
  9.             if (except_flag == EXCEPT_ENTERED) \   
  10.             {  

最重要的部分要数except_raise函数,检查异常是否被处理,如果未被处理,重新从异常栈中弹出新的frame,以跳到更外层的异常处理块。
catch(e)也是宏,检查当前的frame是否和这个catch块中的e对应,如果对应的话,执行下面的部分进行处理。


因为所有except_frame全部放在栈上,因此可以说这个except_stack利用了程序自动产生的stack机制,只要正确地改变Except_stack的值就可以了,不必再考虑分配的except_frame的释放问题,空间的分配释放全由程序自动生成的stack管理。

 

如何使用


使用这个异常机制的代码,如下

c 代码

  1. try{      
  2.   S;      
  3. }catch(e1){      
  4.   S1;      
  5. }catch(e2){      
  6.   S2;      
  7. }else_catch{      
  8.   S3;      
  9. }end_try;   

 此相当于c++中的:

  1. try{      
  2.     S;      
  3. }catch(e1){      
  4.     S1;      
  5. }catch(e2){      
  6.     S2;      
  7. }catch(…){      
  8.    S3;      
  9. }    

当前实现的异常机制也支持finally语句,因此下面的代码:

c代码
  1. try{      
  2.     S;      
  3. }catch(e1){      
  4.     S1;      
  5. }finally{      
  6.     S2;      
  7. }end_try;  

相当于java中的:

java 代码
  1. try{      
  2.     S;      
  3. }catch(e1except e1){      
  4.     S1;      
  5. }finally     
  6.     S2;   

源代码

文件:exception.h

c 代码

  1. #ifndef __EXCEPTION_H__   
  2. #define __EXCEPTION_H__   
  3.   
  4. #include    
  5. #include    
  6. #include    
  7.   
  8.   
  9. #define T Except_t   
  10. typedef struct Except_t{   
  11.         char *reason;   
  12. }Except_t;   
  13.            
  14. typedef struct Except_frame{   
  15.         struct Except_frame *prev;   
  16.         jmp_buf env;   
  17.         const char *file;   
  18.         int line;   
  19.         const T* exception;   
  20. }Except_frame;   
  21.   
  22. extern Except_frame *Except_stack;      //全局变量    
  23.   
  24. //异常的状态常量    
  25. enum {EXCEPT_ENTERED=0,EXCEPT_RAISED,   
  26.      EXCEPT_HANDLED,EXCEPT_FINALIZED};    
  27.   
  28. #define throw(e) except_raise(&(e),__FILE__,__LINE__)   
  29.   
  30. #define rethrow except_raise(except_frame.exception,\   
  31.                    except_frame.file,except_frame.line)   
  32.                       
  33. void abort_without_exception(const Except_t *e,const char *file,int line);   
  34.   
  35. //将栈顶元素从栈中弹出,重新抛出    
  36. void except_raise(const T *e,const char *file,int line);   
  37.               
  38. //try的作用就是将一个包含环境变量env的except_frame压入Except_stack栈中。   
  39. //其中except_flag为setjmp的返回值,表示异常的状态    
  40. #define try do{ \   
  41.             volatile int except_flag; \   
  42.             Except_frame except_frame; \   
  43.             except_frame.prev = Except_stack; \   
  44.             Except_stack = &except_frame; \   
  45.             except_flag = setjmp(except_frame.env); \   
  46.             if (except_flag == EXCEPT_ENTERED) \   
  47.             {   
  48.   
  49. //如果和刚刚压入Except_stack中的except_frame对应的longjmp不发生,   
  50. //就将其从栈里面弹出来,而如果发生的话,就恢复这个环境变量所   
  51. //保存的环境,回到setjmp()的位置重新进行处理,这时候except_flag   
  52. //的值为EXCEPT_RAISED    
  53. #define catch(e) \   
  54.                 if(except_flag == EXCEPT_ENTERED) \   
  55.                     Except_stack = Except_stack->prev; \   
  56.             }else if(except_frame.exception == &(e)){ \   
  57.                 except_flag = EXCEPT_HANDLED;   
  58.   
  59. #define try_return \   
  60.                    switch(Except_stack = Except_stack->prev,0) \   
  61.                        defaultreturn    
  62.   
  63. #define catch_else \   
  64.                    if(except_flag == EXCEPT_ENTERED) \   
  65.                        Except_stack = Except_stack->prev; \   
  66.                    }else{ \   
  67.                      except_flag = EXCEPT_HANDLED;   
  68.   
  69. //如果没有相关的处理函数,重新抛出    
  70. #define end_try \   
  71.                 if(except_flag == EXCEPT_ENTERED) \   
  72.                     Except_stack = Except_stack->prev; \   
  73.                 } \   
  74.                 if (except_flag == EXCEPT_RAISED) \   
  75.                     except_raise(except_frame.exception, \   
  76.                         except_frame.file,except_frame.line); \   
  77.                 }while(0)   
  78.                    
  79.                    
  80. #define finally \   
  81.                 if(except_flag == EXCEPT_ENTERED) \   
  82.                     Except_stack = Except_stack->prev; \   
  83.                 }{ \   
  84.                     if(except_flag == EXCEPT_ENTERED) \   
  85.                         except_flag = EXCEPT_FINALIZED;    
  86.   
  87. #undef T   
  88. #endif  

 文件:exception.c

c 代码
  1. #include "exception.h"   
  2.   
  3. Except_frame *Except_stack = NULL;   
  4.   
  5. void except_raise(const Except_t *e,const char *file,int line)   
  6. {   
  7.      Except_frame *p = Except_stack;   
  8.         
  9.      assert(e);   
  10.      if(p == NULL){   
  11.           abort_without_exception(e,file,line);   
  12.      }   
  13.      p->exception = e;   
  14.      p->file = file;   
  15.      p->line = line;   
  16.      Except_stack = Except_stack->prev;   
  17.      longjmp(p->env,EXCEPT_RAISED);   
  18.  }   
  19.   
  20. void abort_without_exception(const Except_t *e,const char *file,int line)   
  21. {   
  22.      fprintf(stderr,"Uncaught exception");   
  23.      if(e->reason)   
  24.          fprintf(stderr," %s",e->reason);   
  25.      else  
  26.          fprintf(stderr," at 0x%p",e);   
  27.         
  28.      if (file && line > 0)   
  29.          fprintf(stderr, "raised at %s:%d\n",file,line);   
  30.      fprintf(stderr,"aborting...\n");   
  31.      fflush(stderr);   
  32.      abort();   
  33. }  
 

参考资料:

c语言接口与实现》 David R Hanson

 

评论 共 2 条 请登录后发表评论
2 楼 topxuchenghua 2011-01-10 13:50
经典值得学习
1 楼 George_ghc 2010-09-30 09:31
  不错!

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics