TEST

2010年11月16日 星期二

exception使用的時機

一直以來對於 exception 使用的時機不是很清楚,所以從來沒用過,想說反正也有 google約爾等有名大師都說不要用 exception ,所以一直以來都是使用 error code return (或紀錄 error code) 的方式。 但也有人說 exception 不是惡魔,且沒了 exception 就沒有 RAII 這方便的作法。但看了半天 RAII 是跟 exception 綁在一起的嗎?沒有 exception 一樣可以做 RAII ,是沒有 exception 就沒有做 RAII 的理由吧? exception 可以說是一種 goto ,對於程式流程跟可讀性來說,會造成人腦在run程式的複雜度。我也覺得非必要時候不用 exception 。那何時是非必要時?何時又是使用 exception 的時機呢? 這時就必須了解c+++中常用的處理錯誤方法:
  • assert
  • error code
  • exception
在此不闡述,就我自己的理解思考,使用 exception 時機有
  1. 用到 STL 或有拋出 exception 的程式,因你無法保證 100%不會有 exception。
  2. 簡化程式流程的時候,這在預設執行的各 method/function 都是 99.9%正確時可簡化,遇到不能考慮的錯誤時就是那 0.01%。
  3. 子程式不知怎麼處理此錯誤,可能父程式或父父程式以上(最高為使用者)知道如何處理時。
  4. 想回傳error code卻不行時。
避免濫用
  1. 不要在同一 scope throw, catch,這很明顯可以用 if else/switch 來解決,但是很多書上都這樣教。
  2. 父程式可考慮解決的錯誤可以使用 error code。
有時 exception 使用上沒有 error code 來的簡潔,比如說以下這段,你只是要秀錯誤訊息那用 exception 很方便但是要針對錯誤來處理呢?
while (IsSending)
{
 try
    {
        foo->Send(SendData);
        IsSending=false;
        foo->Save(SendData);
    }
    catch(Exception &e)
    {
        ErrorMsg = e.ClassName() + ":" + e.Message;
    }
}
還是使用 error code 的方式吧,我無法想像用exception來處理:
while (IsSending)
{
    ErrorCode error_code;
    error_code=foo->Send(SendData);
    switch(error_code)
    {
    case SEND_OK:
        IsSending=false;
        error_code=foo->Save(SendData);
        switch(error_code)
        {
        case SAVE_OK:
            break;
        case SAVE_NO_FILE:
            //Create a new file
            break;
        case SAVE_BE_LOCK:
            //UnLock or throw if can't be UnLock here
            break;
        }
        break;
    case SEND_NOT_FINISH:
        //Get left SendData  (then Send)
        break;
    case SEND_EMPTY:
        //WaitData or GetData (then Send )
        break;
    case SEND_BE_LOCK:
        //UnLock(then Send) or throw if can't be UnLock here
        break;
    }
}
好吧寫個參考,用exception來處理:
while (IsSending)
{
    try
    {
        foo->Send(SendData);
        IsSending=false;
    }
    catch(Exception &e)
    {
        ErrorCode error_code = e.Code;
        switch(error_code)
        {
        case SEND_NOT_FINISH:
            //Get left SendData  (then Send)
            break;
        case SEND_EMPTY:
            //WaitData or GetData (then Send )
            break;
        case SEND_BE_LOCK:
            //UnLock(then Send) or throw if can't be UnLock here
            break;
        }
    }
    
    try
    {
        foo->Save(SendData);
    }
    catch(Exception &e)
    {
        ErrorCode error_code = e.Code;
        switch(error_code)
        {
        case SAVE_NO_FILE:
            //Create a new file
            break;
        case SAVE_BE_LOCK:
            //UnLock or throw if can't be UnLock here
            break;
        }
    }
}
嗯有比較簡潔嗎? 或許可以改寫成以下方式:
while (IsSending)
{
    try
    {
        foo->Send(SendData);
        IsSending=false;
        foo->Save(SendData);
    }
    catch(Exception &e)
    {
        ErrorCode error_code = e.Code;
        switch(error_code)
        {
        case SEND_NOT_FINISH:
            //Get left SendData  (then Send)
            break;
        case SEND_EMPTY:
            //WaitData or GetData (then Send )
            break;
        case SEND_BE_LOCK:
            //UnLock(then Send) or throw if can't be UnLock here
            break;
            
        case SAVE_NO_FILE:
            IsSending=false;
            //Create a new file
            break;
        case SAVE_BE_LOCK:
            IsSending=false;
            //UnLock or throw if can't be UnLock here
            break;
        }
    }
}
雖然在程式上看來較簡潔,但是對於程式流程卻較複雜,隨著程式碼越多,跳到哪裡都不知道了... 其他不使用ex的原因還有影響效能,團隊編程顧慮等。 但使用error code (status code) 也有以下缺點:
  1. error code是可以被忽略的。
  2. 無法自動向上上層(父父程式)傳播。必須由上層接力在丟一次。
  3. 在程式流程中不能集中處理(某些情況是優點)。
  4. 必須有回傳值或紀錄值。
反過來說你不想要這些缺點也是使用 exception 的時機。

Next: c++ checked exceptions (Exception specifications)?
根據[C++ coding Standards] 書中第 75 項 Avoid exception specifications , 且在 A Pragmatic Look at Exception Specifications 中的建議 "Never write an exception specification." 所以就不研究了,進階可看 Questions About Exception Specifications

[Reference]