起因:在c语言工程代码当中,常常要处理各种关于异常的处理,每次当程序比较复杂的时候,就必须用好几个if来嵌套使用,比如
- if (xxx)
- {
- if(xxx)
- {
- }
- else
- {
- return ERR_NO;
- }
- }
这样的代码显得非常混乱,也不容易管理,我一直在寻找能跟c++异常机制类似的功能,如果有这样的功能,那么c语言的异常处理不是也很容易打理了么?
由于c的工程当中一般错误都有专有的错误列表,所以在这边,在我们的机制里我只添加了关于错误Id的异常捕捉处理。
思路:
首先,我使用了#define来将c当中没有的关键词替代,比如try,catch,throw,先想到的是用return,break之类的c语言当中先有的关键词来模拟,这就有了我的第一个版本:
- #ifndef EXCEPTION_H
- #define EXCEPTION_H
- // 一个不成熟的exception机制
- typedef unsigned int ERR_TYPE;
- #define NO_ERR 0
- static ERR_TYPE err; //try-catch-throw错误处理机制
- #define try do
- #define catch(N) while(0);if(N>=NO_ERR)
- #define throw(N) err=N;break;
- #define throwAgain(N) err=N;return;
- #endif
调用实例代码:
- void f(void)
- {
- try
- {
- throw(6);
- }
- catch(err)
- {
- throwAgain(err);
- }
- }
- int main()
- {
- try
- {
- f();
- }
- catch(err)
- {
- printf("%d", err);
- }
- }
这个设计的程序有一个很大的问题,那就是它只能在同级的函数下使用try。
这是什么意思呢?如果我需要这样使用:
- Int main()
- {
- Try
- {
- f();
- }
- catch(err)
- {
- printf("%d", err);
- }
- }
- Void f()
- {
- G();
- // some codes follows..
- }
- Void g()
- {
- Throw(3);
- }
在g()throw出异常3的时候,main函数没有办法直接捕捉,而是必须在f()函数下面继续执行后面的语句。就是这套设计的机制多个嵌套的函数是没有办法实施的。
那么还有什么办法可以解决这个问题呢?Goto可以吗?不可以,goto也只能在同级的函数下使用。goto是本地的:它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点 。
最后找到一个方法:
- #include <setjmp.h>
- int setjmp(jmp_buf env)
- Returns: 0 if called directly, nonzero if returning from a call to longjmp.
- void longjmp(jmp_buf env, int val);
setjmp 和
longjmp这2个函数可以在不同的函数进行跳转,只要设置他们的上下文,在用longjmp跳转到这设置好的上下文当中就可以了!!
这2个函数的使用方法:
- #include<stdio.h>
- #include<setjmp.h>
- jmp_buf ebuf;
- void f2(void);
- int main(void)
- {
- int i;
- printf("1");
- i=setjmp(ebuf);
- if(i==0)
- {
- f2();
- printf("This will not be printed.");
- }
- printf("%d",i);
- return 0;
- }
- void f2(void)
- {
- printf("2");
- longjmp(ebuf,3);
- }
首先,在函数a里面setjmp,将当前函数的env都存储起来,然后在函数b里面调用longjmp,这样就会直接跳转到函数a的setjmp这个地方,longjmp的第二个值作用是在第二次返回a函数的时候setjmp会返回longjmp的第二个参数。
有了这个方法,我们的程序就好设计多了,由于设计多个函数的调用,所以设计的时候使用了链表来处理保存每个函数的env。
全部实现代码如下:
- #ifndef LIST_H
- #define LIST_H
- typedef struct list_head_tag
- {
- struct list_head_tag *prev;
- struct list_head_tag *next;
- }list_head;
- #define list_for_each(pos, head) \
- for ( pos = (head)->next; \
- pos != (head); \
- pos = pos->next)
- #define list_for_each_safe(pos,pos_next,head) \
- for ( pos = (head)->next, pos_next = pos->next; \
- pos != (head); \
- pos = pos_next, pos_next = pos->next)
- #define list_empty(head) (head->next == head)
- #define list_entry(ptr, type, member) ((type*)ptr)
- #define init_list_head(ptr) \
- do{\
- (ptr)->prev = ptr; \
- (ptr)->next = ptr; \
- }while(0)
- extern void list_add_before(list_head *node, list_head *pos);
- extern void list_add_after(list_head *node, list_head *pos);
- extern void list_del(list_head *node);
- #endif
- #include "list.h"
- void list_add_before(list_head *node, list_head *pos)
- {
- node->prev = pos->prev;
- node->next = pos;
- pos->prev->next = node;
- pos->prev = node;
- }
- void list_add_after(list_head *node, list_head *pos)
- {
- node->prev = pos;
- node->next = pos->next;
- pos->next->prev = node;
- pos->next = node;
- }
- void list_del(list_head *node)
- {
- node->prev->next = node->next;
- node->next->prev = node->prev;
- }
- #ifndef EXC_H
- #define EXC_H
- char err = -1;
- static char isJumpListInit = 0;
- //jmp_buf jump_buffer;
- typedef struct JumpBufListTag
- {
- struct list_head_tag list;
- jmp_buf jump_buffer;
- }JumpBufList, *JumpBufListPtr;
- JumpBufList jumplist = {NULL, NULL};
- JumpBufListPtr head = &jumplist;
- JumpBufListPtr cur = &jumplist;
- int SetCurJump(void)
- {
- JumpBufListPtr newPtr = (JumpBufList*)calloc(sizeof(JumpBufList));
- if (!isJumpListInit)
- {
- init_list_head(&head->list);
- isJumpListInit = 1;
- }
- list_add_after(&newPtr->list, &head->list);
- cur = newPtr;
- return 0;
- }
- void JumpCurLong(void)
- {
- longjmp(cur->jump_buffer, 1);
- }
- void DestoryCurJumpEnv( void )
- {
- list_del(&cur->list);
- free(cur);
- cur = head->list.next;
- }
- #define try SetCurJump();if(setjmp(cur->jump_buffer) == 0)
- #define catch(N) DestoryCurJumpEnv();if(N>=0)
- #define throw(N) err=N;JumpCurLong();
- #endif
这边,List.h和list.c摘抄自linux内核代码。
使用demo代码:
- void h()
- {
- throw(7);
- }
- void e()
- {
- h();
- }
- void g(void)
- {
- try
- {
- e();
- printf("g()3");
- }
- catch(err)
- {
- throw(err);
- }
- }
- int main()
- {
-
- try
- {
- g();
- }
- catch(err)
- {
- printf("%d", err);
- }
- return 0;
- }
这样,就可以实现一个简易版本的用c语言模拟c++的错误处理异常的机制了。
阅读(6268) | 评论(0) | 转发(6) |