▌第一次閱讀本系列的,可以先看:
【本篇是例外處理系列的第一篇。】
目的是希望用 C 模擬 try-catch
機制
▌閱讀本系列的例外處理系列前:
【注意】
之所以說是極粗略實現(標題),是因為在這天之前,沒有真正動手實作例外處理。
只有構思及想法,所以本系列的 例外處理系列 屬於 【實驗性質】,
可否完美/真正實現還是未知之數,需要研究、嘗試以及逐步完善,
所以請不要以此作參考、樣本或榜樣。
除了 【實驗性質】 也存在著 【記錄性質】,
記錄研究、嘗試實作的過程,而這亦是自我挑戰的一部分。
只有構思及想法,所以本系列的 例外處理系列 屬於 【實驗性質】,
可否完美/真正實現還是未知之數,需要研究、嘗試以及逐步完善,
所以請不要以此作參考、樣本或榜樣。
記錄研究、嘗試實作的過程,而這亦是自我挑戰的一部分。
【你可以選擇跳過這幾天不看,直至完善為止。】
▌例外處理的極粗略實現:
目前本系列的實現有一個前提,或者說編程風格約定,
就是函數的回傳值只用作錯誤碼檢測。
默認回傳 -1
代表發生錯誤, 0
代表正常。
這意味輸出數值必須使用按址傳值,不可使用回傳。
以下有巨集 try
, catch
, throw
的實現,
先說 try
和 throw
這兩個比較簡單的實現:
#define try(function, ex_name) \
\
if(function == -1){ \
goto ex_name; \
} \
back_to_##ex_name:
#define throw(function) \
\
if(function == -1){ \
return -1; \
}
巨集 try
有兩個參數,function
和 ex_name
,
一個是會回傳錯誤的函數,
另一個是 例外(exception)的名稱,名稱是自己作的。
根據約定,回傳值只用作錯誤檢測, -1
代表錯誤。
所以 巨集 try
檢測函數的回傳是否等於 -1
,即有否發生錯誤。
若是,則 goto
到 catch
的部分。
安排了標記 back_to_##ex_name:
來讓 catch
返回原本位置。
而 throw
的部分更加簡單,
檢測函數的回傳是否等於 -1
,
若是,則 return -1;
,根據約定,即等於拋出錯誤。
就是函數的回傳值只用作錯誤碼檢測。
-1
代表發生錯誤, 0
代表正常。try
, catch
, throw
的實現,先說
try
和 throw
這兩個比較簡單的實現:#define try(function, ex_name) \
\
if(function == -1){ \
goto ex_name; \
} \
back_to_##ex_name:
#define throw(function) \
\
if(function == -1){ \
return -1; \
}
try
有兩個參數,function
和 ex_name
,一個是會回傳錯誤的函數,
另一個是 例外(exception)的名稱,名稱是自己作的。
-1
代表錯誤。try
檢測函數的回傳是否等於 -1
,即有否發生錯誤。若是,則
goto
到 catch
的部分。安排了標記
back_to_##ex_name:
來讓 catch
返回原本位置。throw
的部分更加簡單,檢測函數的回傳是否等於
-1
,若是,則
return -1;
,根據約定,即等於拋出錯誤。
▌巨集 catch
的部分稍微複雜一點:
#define catch(ex_name, ...) \
\
goto ex_name##_end; \
ex_name: \
__VA_ARGS__ \
goto back_to_##ex_name; \
ex_name##_end:
注意 \
的前面有空格,為避免前後文連在一起,區分出識別字。
巨集 catch
有兩個參數,一個是 try
產生的例外名稱,
另一個是 可變參數,作為輸入錯誤處理的代碼。
共有五行:
goto ex_name##_end;
ex_name:
__VA_ARGS__
goto back_to_##ex_name;
ex_name##_end:
第二行 ex_name:
是 try
產生的例外名稱 的標記,
用於發生錯誤後,被跳轉的位置。
第三行 __VA_ARGS__
是 可變參數宏,
會原封不漏地把錯誤處理的代碼搬到這裏。
第四行 goto back_to_##ex_name;
用於返回發生錯誤的位置。
即對應 try
的標記。
若果程序正常地運行 catch
到語句,
第一行會把程序跳轉到第五行,使其跳過 錯誤處理 的部分。
#define catch(ex_name, ...) \
\
goto ex_name##_end; \
ex_name: \
__VA_ARGS__ \
goto back_to_##ex_name; \
ex_name##_end:
\
的前面有空格,為避免前後文連在一起,區分出識別字。catch
有兩個參數,一個是 try
產生的例外名稱,另一個是 可變參數,作為輸入錯誤處理的代碼。
goto ex_name##_end;
ex_name:
__VA_ARGS__
goto back_to_##ex_name;
ex_name##_end:
ex_name:
是 try
產生的例外名稱 的標記,用於發生錯誤後,被跳轉的位置。
__VA_ARGS__
是 可變參數宏,會原封不漏地把錯誤處理的代碼搬到這裏。
goto back_to_##ex_name;
用於返回發生錯誤的位置。即對應
try
的標記。catch
到語句,第一行會把程序跳轉到第五行,使其跳過 錯誤處理 的部分。
▌來一個使用例子:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*... 巨集 try, catch, throw 的宣告 ...*/
int can_not_be_negative(double input) {
return input < 0 ? -1 : 0;
}
int my_sqrt(double *output, double input) {
throw(can_not_be_negative(input));
*output = sqrt(input);
return 0;
}
int main() {
double get_output;
try(my_sqrt(&get_output, -10), ex_neg);
/*... any things ...*/
catch(ex_neg, printf("Input can't no be negative.\n"); );
system("pause");
return 0;
}
Input can't no be negative.
Press any key to continue . . .
函數 can_not_be_negative
會返回錯誤(-1
),
函數 my_sqrt
會用 throw
拋出 函數 can_not_be_negative
產生的錯誤,
try
接收了 my_sqrt
拋出的錯誤,
程序跳過 /*... any things ...*/
,到 catch
進行錯誤處理,
返回 try
後的語句。
主程序可改寫成比較像 try{}-catch{}
的區塊形式,稍微增加可讀性:
int main() {
double get_output;
try(
my_sqrt(&get_output, -10),
ex_neg);
/*... any things ...*/
catch(ex_neg,
printf("Input can't no be negative.\n");
);
system("pause");
return 0;
}
目前有不少問題,例如一個 try
必須獨立對應一個 catch
,
並且當中的 例外名稱 需要唯一。
故此 多個同一類型的錯誤 會產生多個重複語句。
十個 try
有 十個 catch
。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*... 巨集 try, catch, throw 的宣告 ...*/
int can_not_be_negative(double input) {
return input < 0 ? -1 : 0;
}
int my_sqrt(double *output, double input) {
throw(can_not_be_negative(input));
*output = sqrt(input);
return 0;
}
int main() {
double get_output;
try(my_sqrt(&get_output, -10), ex_neg);
/*... any things ...*/
catch(ex_neg, printf("Input can't no be negative.\n"); );
system("pause");
return 0;
}
Input can't no be negative.
Press any key to continue . . .
can_not_be_negative
會返回錯誤(-1
),函數
my_sqrt
會用 throw
拋出 函數 can_not_be_negative
產生的錯誤,try
接收了 my_sqrt
拋出的錯誤,程序跳過
/*... any things ...*/
,到 catch
進行錯誤處理,返回
try
後的語句。try{}-catch{}
的區塊形式,稍微增加可讀性:int main() {
double get_output;
try(
my_sqrt(&get_output, -10),
ex_neg);
/*... any things ...*/
catch(ex_neg,
printf("Input can't no be negative.\n");
);
system("pause");
return 0;
}
try
必須獨立對應一個 catch
,並且當中的 例外名稱 需要唯一。
故此 多個同一類型的錯誤 會產生多個重複語句。
十個
try
有 十個 catch
。
0 Comments
發佈留言