iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 24
0
自我挑戰組

30 Days 如何把 C 語言偽裝成高階語言 OWO /系列 第 25

Day 24: 例外處理系列:極粗略實現、使用例子

▌第一次閱讀本系列的,可以先看:

本系列的大綱 傳送門


【本篇是例外處理系列的第一篇。】

目的是希望用 C 模擬 try-catch 機制


▌閱讀本系列的例外處理系列前:

對巨集 ## 不熟悉的:
Day 21:重溫前置處理器、巨集( #, ## )、預先定義的巨集

對可變參數宏 __VA_ARGS__ 不熟悉的:
Day 22:重溫 可變參數函數、可變參數宏 __VA_ARGS__

goto 的用法不熟悉的:
Day 23: goto、標記、爭議及反面例子



【注意】

之所以說是極粗略實現(標題),是因為在這天之前,沒有真正動手實作例外處理。
只有構思及想法,所以本系列的 例外處理系列 屬於 【實驗性質】
可否完美/真正實現還是未知之數,需要研究嘗試以及逐步完善
所以請不要以此作參考、樣本或榜樣。


除了 【實驗性質】 也存在著 【記錄性質】
記錄研究、嘗試實作的過程,而這亦是自我挑戰的一部分。


【你可以選擇跳過這幾天不看,直至完善為止。】



▌例外處理的極粗略實現:

目前本系列的實現有一個前提,或者說編程風格約定
就是函數的回傳值只用作錯誤碼檢測

默認回傳 -1 代表發生錯誤, 0 代表正常。

這意味輸出數值必須使用按址傳值,不可使用回傳。


以下有巨集 try, catch, throw 的實現,
先說 trythrow 這兩個比較簡單的實現:

#define try(function, ex_name) \
\
if(function == -1){ \
	goto ex_name; \
} \
back_to_##ex_name:
#define throw(function) \
\
if(function == -1){ \
	return -1; \
}

巨集 try 有兩個參數,functionex_name
一個是會回傳錯誤的函數,
另一個是 例外(exception)的名稱,名稱是自己作的

根據約定,回傳值只用作錯誤檢測, -1 代表錯誤。

所以 巨集 try 檢測函數的回傳是否等於 -1即有否發生錯誤
若是,則 gotocatch 的部分。
安排了標記 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 產生的例外名稱,
另一個是 可變參數,作為輸入錯誤處理的代碼。

共有五行:

  1. goto ex_name##_end;
  2. ex_name:
  3. __VA_ARGS__
  4. goto back_to_##ex_name;
  5. 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


很艱難的幾天呀,要花時間摸索。

有什麼方向、建議、意見麻煩留言,謝謝了。

/images/emoticon/emoticon41.gif


上一篇
Day 23: goto、標記、爭議及反面例子
下一篇
Day 25: 例外處理系列:研究進行中:優化:一個 catch 可對應多個 try
系列文
30 Days 如何把 C 語言偽裝成高階語言 OWO /31

尚未有邦友留言

立即登入留言