Chinaunix首页 | 论坛 | 博客
  • 博客访问: 521092
  • 博文数量: 257
  • 博客积分: 1666
  • 博客等级: 上尉
  • 技术积分: 1535
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-02 23:02
文章分类

全部博文(257)

文章存档

2013年(2)

2012年(255)

分类:

2012-09-06 12:41:17

起因:在c语言工程代码当中,常常要处理各种关于异常的处理,每次当程序比较复杂的时候,就必须用好几个if来嵌套使用,比如


点击(此处)折叠或打开

  1. if (xxx)
  2. {
  3.      if(xxx)
  4.      {
  5.      }
  6.       else
  7.        {
  8.              return ERR_NO;
  9.        }
  10. }

这样的代码显得非常混乱,也不容易管理,我一直在寻找能跟c++异常机制类似的功能,如果有这样的功能,那么c语言的异常处理不是也很容易打理了么?

由于c的工程当中一般错误都有专有的错误列表,所以在这边,在我们的机制里我只添加了关于错误Id的异常捕捉处理。

思路:

首先,我使用了#define来将c当中没有的关键词替代,比如try,catch,throw,先想到的是用return,break之类的c语言当中先有的关键词来模拟,这就有了我的第一个版本:


点击(此处)折叠或打开

  1. #ifndef EXCEPTION_H
  2. #define EXCEPTION_H
  3. // 一个不成熟的exception机制
  4. typedef unsigned int    ERR_TYPE;

  5. #define NO_ERR 0
  6. static ERR_TYPE err; //try-catch-throw错误处理机制

  7. #define try do
  8. #define catch(N) while(0);if(N>=NO_ERR)
  9. #define throw(N) err=N;break;
  10. #define throwAgain(N) err=N;return;

  11. #endif
调用实例代码:


点击(此处)折叠或打开

  1. void f(void)
  2. {
  3.     try
  4.     {
  5.         throw(6);
  6.     }
  7.     catch(err)
  8.     {
  9.         throwAgain(err);
  10.     }
  11. }

  12. int main()
  13. {
  14.     try
  15.     {
  16.         f();
  17.     }
  18.     catch(err)
  19.     {
  20.         printf("%d", err);
  21.     }
  22. }

这个设计的程序有一个很大的问题,那就是它只能在同级的函数下使用try。

这是什么意思呢?如果我需要这样使用:


点击(此处)折叠或打开

  1. Int main()
  2. {
  3.     Try
  4.     {
  5.      f();
  6.     }
  7.     catch(err)
  8.     {
  9.         printf("%d", err);
  10.     }
  11. }
  12. Void f()
  13. {
  14. G();
  15. // some codes follows..
  16. }
  17. Void g()
  18. {
  19.     Throw(3);
  20. }

在g()throw出异常3的时候,main函数没有办法直接捕捉,而是必须在f()函数下面继续执行后面的语句。就是这套设计的机制多个嵌套的函数是没有办法实施的。

那么还有什么办法可以解决这个问题呢?Goto可以吗?不可以,goto也只能在同级的函数下使用。goto是本地的:它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点 

最后找到一个方法:



点击(此处)折叠或打开

  1. #include <setjmp.h>

  2. int setjmp(jmp_buf env)
  3. Returns: 0 if called directly, nonzero if returning from a call to longjmp.
  4. void longjmp(jmp_buf env, int val);
setjmp 和longjmp这2个函数可以在不同的函数进行跳转,只要设置他们的上下文,在用longjmp跳转到这设置好的上下文当中就可以了!!

这2个函数的使用方法:


点击(此处)折叠或打开

  1. #include<stdio.h> 
  2. #include<setjmp.h> 
  3. jmp_buf ebuf; 
  4. void f2(void); 
  5. int main(void) 
  6. { 
  7.  int i; 
  8.  printf("1"); 
  9.  i=setjmp(ebuf); 
  10.  if(i==0)
  11.  { 
  12.    f2(); 
  13.    printf("This will not be printed."); 
  14. } 
  15.  printf("%d",i); 
  16.  return 0;
  17. } 
  18.  void f2(void)
  19. { 
  20.  printf("2"); 
  21.  longjmp(ebuf,3);
  22. }

首先,在函数a里面setjmp,将当前函数的env都存储起来,然后在函数b里面调用longjmp,这样就会直接跳转到函数a的setjmp这个地方,longjmp的第二个值作用是在第二次返回a函数的时候setjmp会返回longjmp的第二个参数。

有了这个方法,我们的程序就好设计多了,由于设计多个函数的调用,所以设计的时候使用了链表来处理保存每个函数的env。

全部实现代码如下:


点击(此处)折叠或打开

  1. #ifndef LIST_H
  2. #define LIST_H
  3. typedef struct list_head_tag
  4. {
  5.   struct list_head_tag *prev;
  6.   struct list_head_tag *next;
  7. }list_head;

  8. #define list_for_each(pos, head) \
  9.     for ( pos = (head)->next; \
  10.           pos != (head); \
  11.           pos = pos->next)

  12. #define list_for_each_safe(pos,pos_next,head) \
  13.     for ( pos = (head)->next, pos_next = pos->next; \
  14.           pos != (head); \
  15.           pos = pos_next, pos_next = pos->next)

  16. #define list_empty(head) (head->next == head)

  17. #define list_entry(ptr, type, member) ((type*)ptr)

  18. #define init_list_head(ptr) \
  19.     do{\
  20.         (ptr)->prev = ptr; \
  21.         (ptr)->next = ptr; \
  22.     }while(0)

  23. extern void list_add_before(list_head *node, list_head *pos);

  24. extern void list_add_after(list_head *node, list_head *pos);

  25. extern void list_del(list_head *node);
  26. #endif

点击(此处)折叠或打开

  1. #include "list.h"
  2. void list_add_before(list_head *node, list_head *pos)
  3. {
  4. node->prev = pos->prev;
  5. node->next = pos;
  6. pos->prev->next = node;
  7. pos->prev = node;
  8. }
  9. void list_add_after(list_head *node, list_head *pos)
  10. {
  11. node->prev = pos;
  12. node->next = pos->next;
  13. pos->next->prev = node;
  14. pos->next = node;
  15. }
  16. void list_del(list_head *node)
  17. {
  18. node->prev->next = node->next;
  19. node->next->prev = node->prev;
  20. }

点击(此处)折叠或打开

  1. #ifndef EXC_H
  2. #define EXC_H
  3. char err = -1;
  4. static char isJumpListInit = 0;
  5. //jmp_buf jump_buffer;
  6. typedef struct JumpBufListTag
  7. {
  8. struct list_head_tag list;
  9. jmp_buf jump_buffer;
  10. }JumpBufList, *JumpBufListPtr;
  11. JumpBufList jumplist = {NULL, NULL};
  12. JumpBufListPtr head = &jumplist;
  13. JumpBufListPtr cur = &jumplist;
  14. int SetCurJump(void)
  15. {
  16. JumpBufListPtr newPtr = (JumpBufList*)calloc(sizeof(JumpBufList));
  17. if (!isJumpListInit)
  18. {
  19. init_list_head(&head->list);
  20. isJumpListInit = 1;
  21. }
  22. list_add_after(&newPtr->list, &head->list);
  23. cur = newPtr;
  24. return 0;
  25. }
  26. void JumpCurLong(void)
  27. {
  28. longjmp(cur->jump_buffer, 1);
  29. }
  30. void DestoryCurJumpEnv( void )
  31. {
  32. list_del(&cur->list);
  33. free(cur);
  34. cur = head->list.next;
  35. }
  36. #define try SetCurJump();if(setjmp(cur->jump_buffer) == 0)
  37. #define catch(N) DestoryCurJumpEnv();if(N>=0)
  38. #define throw(N) err=N;JumpCurLong();
  39. #endif
这边,List.h和list.c摘抄自linux内核代码。

使用demo代码:


点击(此处)折叠或打开

  1. void h()
  2. {
  3.     throw(7);
  4. }
  5. void e()
  6. {
  7.     h();
  8. }
  9. void g(void)
  10. {
  11.     try
  12.     {
  13.     e();
  14.     printf("g()3");
  15.     }
  16.     catch(err)
  17.     {
  18.         throw(err);
  19.     }
  20. }
  21. int main()
  22. {
  23.     
  24.     try
  25.     {
  26.         g();

  27.     }
  28.     catch(err)
  29.     {
  30.         printf("%d", err);
  31.     }
  32.     return 0;
  33. }
这样,就可以实现一个简易版本的用c语言模拟c++的错误处理异常的机制了。

阅读(368) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~