2016年4月30日 星期六

c 模擬 try-catch-throw 的運作

在 C 程式裡, 並沒有 try-catch-throw 的語法, 但透過 setjmp 與 longjmp 搭配 switch-case 語法便可以模擬出它的動作. setjmp( myjmp ) 需先設定好 jmp_buf 型別的變數 myjmp, 他作用是單純將下一個程式碼的位置存到變數中, 第一次結束後會傳回 0, 而 longjmp 則是利用剛剛 setjmp 的變數內容加上回傳碼(return code),透過 return 方式讓程式直接轉移到剛剛 setjmp 所處的地方. 但此時 setjmp 的傳回直是由 longjmp 所指定的, 先看以下程式碼:

            enum MyExcept {  TryBegin=0, e1,e2,e3    };
            int main() {
            jmp_buf myjump;      // 先宣告一個 jmp_buf 變數
        // try block setting buffer
       switch( setjmp(myjump)  ) { // 呼叫 setjmp 來保存 programm counter, 第一次結束後傳回 0
case TryBegin:  // 因此 setjmp 完, 開始執行, 此段區塊類似 try 內部的程式區塊
        printf("Try begin: \n");

// throw any exccep here, 在此區塊內(包含所呼叫的副程式也行), 可以任意丟出任何錯誤碼, 讓此區塊外部的 catch 區塊去執行
longjmp(myjump,e2); // longjmp 類似 Throw 指令, 設定 setjmp 回傳碼由第二個參數取代
                                // 在 try 區塊內如果有丟出任何錯誤碼, 這底下一直到 break間的程式碼是不會執行的
printf("It shuold not happen if throw executed\n");
break;
// catch block, 以後的每一 case 區塊, 就類似是一段 catch 區塊
case e1:printf("e1 happen\n");
break;

case e2:
printf("e2 happen\n");
break;

case e3:
printf("e3 happen\n");
break;

default:
printf("e? Not defined\n");
break;

} // Endof try-catch-throw, 這是  try 的最後一段程式碼

    return 0;
}

如果將上述區快改以 macro 來寫就變成:

  enum MyExcept {  TryBegin=0, e1,e2,e3    };
  #define  jmp_buf myjump; Try switch( setjmp(myjump)  )    {   case TryBegin:
  #define  Throw(e) longjmp(myjump, e)
  #define  Catch(e)       break; case (e):
  #define  Endtry           }
  main()
   {
        Try  {
printf("Macro begin:\n");
Throw(e3);
}Catch (e1) {
printf("Error %d\n",e1);
}Catch (e2) {
printf("Error %d\n",e2);
}Catch (e3) {
printf("Error %d\n",e3);
}Endtry

    return 0; 
}

// 程式輸出:
Macro begin:
Error 3


如果將 switch-case 改成用 if-else 來寫就變成:
 enum MyExcept {  TryBegin=0, e1,e2,e3    };
#define Try jmp_buf myjump;int my_a=setjmp(myjump);if( my_a==0 )
 #define Throw(e) longjmp(myjump, e)
 #define Catch(e)  else if( my_a==e     )

  main()
  {
Try {
printf("Macro2 begin:\n");
Throw(e2);
}Catch(e1) {
printf("Error %d\n",e1);
}Catch(e2) {
printf("Error %d\n",e2);
}Catch(e3) {
printf("Error %d\n",e3);
}

    return 0;
  }

// 程式輸出:
Macro2 begin:
Error 2


p.s. longjmp(myjump,value) 如果設定傳回值 value 是 0, 將會自動被改成 1, 避免造成無限循環






沒有留言: