不要忘記,前面我們得出過結論,C語言中提供的這種異常處理機制,與C++中的異常處理模型很相似。例如,可以定義出類似的try block(受到監控的代碼);catch block(異常錯誤的處理模塊);以及可以隨時拋出的異常(throw語句)。所以說,我們可以通過一種非常有技巧的封裝,來達到對setjmp和longjmp的使用方法(或者說語法規則),基本與C++中的語法一致。很有誘惑吧!
首先展示阿愚封裝的在C語言環境中異常處理框架
1、首先是接口的頭文件,主要采用“宏”技術!代碼如下:
/*************************************************
* author: 王勝祥 *
* email: <mantx@21cn.com> *
* date: 2005-03-07 *
* version: *
* filename: ceh.h *
*************************************************/
/********************************************************************
This file is part of CEH(Exception Handling in C Language).
CEH is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
CEH is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
注意:這個異常處理框架不支持線程安全,不能在多線程的程序環境下使用。
如果您想在多線程的程序中使用它,您可以自己試著來繼續完善這個
框架模型。
*********************************************************************/
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <string.h>
////////////////////////////////////////////////////
/* 與異常有關的結構體定義 */
typedef struct _CEH_EXCEPTION {
int err_type; /* 異常類型 */
int err_code; /* 錯誤代碼 */
char err_msg[80]; /* 錯誤信息 */
}CEH_EXCEPTION; /* 異常對象 */
typedef struct _CEH_ELEMENT {
jmp_buf exec_status;
CEH_EXCEPTION ex_info;
struct _CEH_ELEMENT* next;
} CEH_ELEMENT; /* 存儲異常對象的鏈表元素 */
////////////////////////////////////////////////////
////////////////////////////////////////////////////
/* 內部接口定義,操縱維護鏈表數據結構 */
extern void CEH_push(CEH_ELEMENT* ceh_element);
extern CEH_ELEMENT* CEH_pop();
extern CEH_ELEMENT* CEH_top();
extern int CEH_isEmpty();
////////////////////////////////////////////////////
/* 以下是外部接口的定義 */
////////////////////////////////////////////////////
/* 拋出異常 */
extern void thrower(CEH_EXCEPTION* e);
/* 拋出異常 (throw)
a表示err_type
b表示err_code
c表示err_msg
*/
#define throw(a, b, c)
{
CEH_EXCEPTION ex;
memset(&ex, 0, sizeof(ex));
ex.err_type = a;
ex.err_code = b;
strncpy(ex.err_msg, c, sizeof(c));
thrower(&ex);
}
/* 重新拋出原來的異常 (rethrow)*/
#define rethrow thrower(ceh_ex_info)
////////////////////////////////////////////////////
////////////////////////////////////////////////////
/* 定義try block(受到監控的代碼)*/
#define try
{
int ___ceh_b_catch_found, ___ceh_b_occur_exception;
CEH_ELEMENT ___ceh_element;
CEH_EXCEPTION* ceh_ex_info;
memset(&___ceh_element, 0, sizeof(___ceh_element));
CEH_push(&___ceh_element);
ceh_ex_info = &___ceh_element.ex_info;
___ceh_b_catch_found = 0;
if (!(___ceh_b_occur_exception=setjmp(___ceh_element.exec_status)))
{
/* 定義catch block(異常錯誤的處理模塊)
catch表示捕獲所有類型的異常
*/
#define catch
}
else
{
CEH_pop();
___ceh_b_catch_found = 1;
/* end_try表示前面定義的try block和catch block結束 */
#define end_try
}
{
/* 沒有執行到任何的catch塊中 */
if(!___ceh_b_catch_found)
{
CEH_pop();
/* 出現了異常,但沒有捕獲到任何異常 */
if(___ceh_b_occur_exception) thrower(ceh_ex_info);
}
}
}
/* 定義catch block(異常錯誤的處理模塊)
catch_part表示捕獲一定范圍內的異常
*/
#define catch_part(i, j)
}
else if(ceh_ex_info->err_type>=i && ceh_ex_info->err_type<=j)
{
CEH_pop();
___ceh_b_catch_found = 1;
/* 定義catch block(異常錯誤的處理模塊)
catch_one表示只捕獲一種類型的異常
*/
#define catch_one(i)
}
else if(ceh_ex_info->err_type==i)
{
CEH_pop();
___ceh_b_catch_found = 1;
////////////////////////////////////////////////////
////////////////////////////////////////////////////
/* 其它可選的接口定義 */
extern void CEH_init();
////////////////////////////////////////////////////
2、另外還有一個簡單的實現文件,主要實現功能封裝。代碼如下:
/*************************************************
* author: 王勝祥 *
* email: <mantx@21cn.com> *
* date: 2005-03-07 *
* version: *
* filename: ceh.c *
*************************************************/
/********************************************************************
This file is part of CEH(Exception Handling in C Language).
CEH is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
CEH is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
注意:這個異常處理框架不支持線程安全,不能在多線程的程序環境下使用。
如果您想在多線程的程序中使用它,您可以自己試著來繼續完善這個
框架模型。
*********************************************************************/
#include "ceh.h"
////////////////////////////////////////////////////
static CEH_ELEMENT* head = 0;
/* 把一個異常插入到鏈表頭中 */
void CEH_push(CEH_ELEMENT* ceh_element)
{
if(head) ceh_element->next = head;
head = ceh_element;
}
/* 從鏈表頭中,刪除并返回一個異常 */
CEH_ELEMENT* CEH_pop()
{
CEH_ELEMENT* ret = 0;
ret = head;
head = head->next;
return ret;
}
/* 從鏈表頭中,返回一個異常 */
CEH_ELEMENT* CEH_top()
{
return head;
}
/* 鏈表中是否有任何異常 */
int CEH_isEmpty()
{
return head==0;
}
////////////////////////////////////////////////////
////////////////////////////////////////////////////
/* 缺省的異常處理模塊 */
static void CEH_uncaught_exception_handler(CEH_EXCEPTION *ceh_ex_info)
{
printf("捕獲到一個未處理的異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
fprintf(stderr, "程序終止! ");
fflush(stderr);
exit(EXIT_FAILURE);
}
////////////////////////////////////////////////////
////////////////////////////////////////////////////
/* 拋出異常 */
void thrower(CEH_EXCEPTION* e)
{
CEH_ELEMENT *se;
if (CEH_isEmpty()) CEH_uncaught_exception_handler(e);
se = CEH_top();
se->ex_info.err_type = e->err_type;
se->ex_info.err_code = e->err_code;
strncpy(se->ex_info.err_msg, e->err_msg, sizeof(se->ex_info.err_msg));
longjmp(se->exec_status, 1);
}
////////////////////////////////////////////////////
////////////////////////////////////////////////////
static void fphandler( int sig, int num )
{
_fpreset();
switch( num )
{
case _FPE_INVALID:
throw(-1, num, "Invalid number" );
case _FPE_OVERFLOW:
throw(-1, num, "Overflow" );
case _FPE_UNDERFLOW:
throw(-1, num, "Underflow" );
case _FPE_ZERODIVIDE:
throw(-1, num, "Divide by zero" );
default:
throw(-1, num, "Other floating point error" );
}
}
void CEH_init()
{
_control87( 0, _MCW_EM );
if( signal( SIGFPE, fphandler ) == SIG_ERR )
{
fprintf( stderr, "Couldn't set SIGFPE " );
abort();
}
}
////////////////////////////////////////////////////
體驗上面設計出的異常處理框架
請花點時間仔細揣摩一下上面設計出的異常處理框架。呵呵!程序員朋友們,大家是不是發現它與C++提供的異常處理模型非常相似。例如,它提供的基本接口有 try、catch、以及throw等三條語句。還是先看個具體例子吧!以便驗證一下這個C語言環境中異常處理框架是否真的比較好用。代碼如下:
#include "ceh.h"
int main(void)
{
//定義try block塊
try
{
int i,j;
printf("異常出現前 ");
// 拋出一個異常
// 其中第一個參數,表示異常類型;第二個參數表示錯誤代碼
// 第三個參數表示錯誤信息
throw(9, 15, "出現某某異常");
printf("異常出現后 ");
}
//定義catch block塊
catch
{
printf("catch塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
// 這里稍有不同,需要定義一個表示當前的try block結束語句
// 它主要是清除相應的資源
end_try
}
注意,上面的測試程序可是C語言環境下的程序(文件的擴展名請使用.c結尾),雖然它看上去很像C++程序。請編譯運行一下,發現它是不是運行結果如下:
異常出現前
catch塊,被執行到
捕獲到一個異常,錯誤原因是:出現某某異常! err_type:9 err_code:15
呵呵!程序的確是在按照我們預想的流程在執行。再次提醒,這可是C程序,但是它的異常處理卻非常類似于C++中的風格,要知道,做到這一點其實非常地不容易。當然,上面異常對象的傳遞只是在一個函數的內部,同樣,它也適用于多個嵌套函數間的異常傳遞,還是用代碼驗證一下吧!在上面的代碼基礎下,小小修改一點,代碼如下:
#include "ceh.h"
void test1()
{
throw(0, 20, "hahaha");
}
void test()
{
test1();
}
int main(void)
{
try
{
int i,j;
printf("異常出現前 ");
// 注意,這個函數的內部會拋出一個異常。
test();
throw(9, 15, "出現某某異常");
printf("異常出現后 ");
}
catch
{
printf("catch塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
}
同樣,在上面程序中,test1()函數內拋出的異常,可以被上層main()函數中的catch block中捕獲到。運行結果就不再給出了,大家可以自己編譯運行一把,看看運行結果。
另外這個異常處理框架,與C++中的異常處理模型類似,它也支持try catch塊的多層嵌套。很厲害吧!還是看演示代碼吧!,如下:
#include "ceh.h"
int main(void)
{
// 外層的try catch塊
try
{
// 內層的try catch塊
try
{
throw(1, 15, "嵌套在try塊中");
}
catch
{
printf("內層的catch塊被執行 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
printf("外層的catch塊被執行 ");
}
end_try
throw(2, 30, "再拋一個異常");
}
catch
{
printf("外層的catch塊被執行 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
}
請編譯運行一下,程序的運行結果如下:
內層的catch塊被執行
捕獲到一個異常,錯誤原因是:嵌套在try塊中! err_type:1 err_code:15
外層的catch塊被執行
捕獲到一個異常,錯誤原因是:再拋一個異常! err_type:2 err_code:30
還有,這個異常處理框架也支持對異常的分類處理。這一點,也完全是模仿C++中的異常處理模型。不過,由于C語言中,不支持函數名重載,所以語法上略有不同,還是看演示代碼吧!,如下:
#include "ceh.h"
int main(void)
{
try
{
int i,j;
printf("異常出現前 ");
throw(9, 15, "出現某某異常");
printf("異常出現后 ");
}
// 這里表示捕獲異常類型從4到6的異常
catch_part(4, 6)
{
printf("catch_part(4, 6)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
// 這里表示捕獲異常類型從9到10的異常
catch_part(9, 10)
{
printf("catch_part(9, 10)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
// 這里表示只捕獲異常類型為1的異常
catch_one(1)
{
printf("catch_one(1)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
// 這里表示捕獲所有類型的異常
catch
{
printf("catch塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
}
請編譯運行一下,程序的運行結果如下:
異常出現前
catch_part(9, 10)塊,被執行到
捕獲到一個異常,錯誤原因是:出現某某異常! err_type:9 err_code:15
與C++中的異常處理模型相似,它這里的對異常的分類處理不僅支持一維線性的;同樣,它也支持分層的,也即在當前的try catch塊中找不到相應的catch block,那么它將會到上一層的try catch塊中繼續尋找。演示代碼如下:
#include "ceh.h"
int main(void)
{
try
{
try
{
throw(1, 15, "嵌套在try塊中");
}
catch_part(4, 6)
{
printf("catch_part(4, 6)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
printf("這里將不會被執行到 ");
}
catch_part(2, 3)
{
printf("catch_part(2, 3)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
// 找到了對應的catch block
catch_one(1)
{
printf("catch_one(1)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
catch
{
printf("catch塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
}
到目前為止,大家是不是已經覺得,這個主人公阿愚封裝的在C語言環境中異常處理框架,已經與C++中的異常處理模型95%相似。無論是它的語法結構;還是所完成的功能;以及它使用上的靈活性等。下面我們來看一個各種情況綜合的例子吧!代碼如下:
#include "ceh.h"
void test1()
{
throw(0, 20, "hahaha");
}
void test()
{
test1();
}
int main(void)
{
try
{
test();
}
catch
{
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
try
{
try
{
throw(1, 15, "嵌套在try塊中");
}
catch
{
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
throw(2, 30, "再拋一個異常");
}
catch
{
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
try
{
throw(0, 20, "嵌套在catch塊中");
}
catch
{
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
end_try
}
end_try
}
請編譯運行一下,程序的運行結果如下:
捕獲到一個異常,錯誤原因是:hahaha! err_type:0 err_code:20
捕獲到一個異常,錯誤原因是:嵌套在try塊中! err_type:1 err_code:15
捕獲到一個異常,錯誤原因是:再拋一個異常! err_type:2 err_code:30
捕獲到一個異常,錯誤原因是:嵌套在catch塊中! err_type:0 err_code:20
最后,為了體會到這個異常處理框架,更進一步與C++中的異常處理模型相似。那就是它還支持異常的重新拋出,以及系統中能捕獲并處理程序中沒有catch到的異常。看代碼吧!如下:
#include "ceh.h"
void test1()
{
throw(0, 20, "hahaha");
}
void test()
{
test1();
}
int main(void)
{
// 這里表示程序中將捕獲浮點數計算異常
CEH_init();
try
{
try
{
try
{
double i,j;
j = 0;
// 這里出現浮點數計算異常
i = 1/j ;
test();
throw(9, 15, "出現某某異常");
}
end_try
}
catch_part(4, 6)
{
printf("catch_part(4, 6)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
catch_part(2, 3)
{
printf("catch_part(2, 3)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
}
// 捕獲到上面的異常
catch
{
printf("內層的catch塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
// 這里再次把上面的異常重新拋出
rethrow;
printf("這里將不會被執行到 ");
}
end_try
}
catch_part(7, 9)
{
printf("catch_part(7, 9)塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
throw(2, 15, "出現某某異常");
}
// 再次捕獲到上面的異常
catch
{
printf("外層的catch塊,被執行到 ");
printf("捕獲到一個異常,錯誤原因是:%s! err_type:%d err_code:%d ",
ceh_ex_info->err_msg, ceh_ex_info->err_type, ceh_ex_info->err_code);
// 最后又拋出了一個異常,
// 但是這個異常沒有對應的catch block處理,所以系統中處理了
throw(2, 15, "出現某某異常");
}
end_try
}
請編譯運行一下,程序的運行結果如下:
內層的catch塊,被執行到
捕獲到一個異常,錯誤原因是:Divide by zero! err_type:-1 err_code:131
外層的catch塊,被執行到
捕獲到一個異常,錯誤原因是:Divide by zero! err_type:-1 err_code:131
捕獲到一個未處理的異常,錯誤原因是:出現某某異常! err_type:2 err_code:15
程序終止!