try-catch 機制對巨集 ## 不熟悉的:
Day 21:重溫前置處理器、巨集( #, ## )、預先定義的巨集
對可變參數宏 __VA_ARGS__ 不熟悉的:
Day 22:重溫 可變參數函數、可變參數宏 __VA_ARGS__
對 goto 的用法不熟悉的:
Day 23: goto、標記、爭議及反面例子
之所以說是極粗略實現(標題),是因為在這天之前,沒有真正動手實作例外處理。
只有構思及想法,所以本系列的 例外處理系列 屬於 【實驗性質】,
可否完美/真正實現還是未知之數,需要研究、嘗試以及逐步完善,
所以請不要以此作參考、樣本或榜樣。
除了 【實驗性質】 也存在著 【記錄性質】,
記錄研究、嘗試實作的過程,而這亦是自我挑戰的一部分。
目前本系列的實現有一個前提,或者說編程風格約定,
就是函數的回傳值只用作錯誤碼檢測。
默認回傳 -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 到語句,
第一行會把程序跳轉到第五行,使其跳過 錯誤處理 的部分。
#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 。
![]()