2012-09-15 93 views
1

在編寫不同編碼的字符串(例如從UTF-8到UTF-16)之間進行轉換的函數時,處理錯誤的最佳方式是什麼(例如無效的輸入UTF-8字節序列)?拋出異常或返回錯誤代碼(甚至是bool)?字符串轉換錯誤:異常或錯誤代碼?

// Throws a C++ exception on error. 
std::wstring ConvertFromUtf8ToUtf16(const std::string& utf8); 

// Returns true on success, false on error. 
bool ConvertFromUtf8ToUtf16(std::wstring& utf16, const std::string& utf8); 

使用異常,可能會進行鏈接函數調用(當函數返回值用作其他函數/方法的輸入時)。

但我不確定在這種情況下使用異常是好的;我在想Eric Lippert in his quality blog post叫什麼難以忍受的例外(和相關的Int32.Parse()/TryParse()例子)。

例如,如果使用異常,調用者要用力包裹在try/catch塊函數調用來檢查無效的UTF-8輸入的情況下:

try 
{ 
    wstring utf16 = ConvertFromUtf8ToUtf16(utf8); 
} 
catch(const Utf8ConversionException& e) 
{ 
    // Bad UTF-8 byte sequence 
    ... 
} 

這似乎並不理想給我。

也許做的最好的事情是隻提供重載(實現在不拋出過載轉換代碼,並在投擲超載只是調用不拋出版本,並在錯誤返回碼的情況下拋出異常)?

+0

從utf-8到utf-16的轉換可能發生什麼錯誤? (提示:驗證輸入應與轉換完全分開) –

+1

如果轉換爲UTF-16,結果應該是'std :: u16string',而不是'std :: wstring'。後者有[非常具體的目的](http://stackoverflow.com/questions/6300804/wchars-encodings-standards-and-portability)。 –

+2

假設從UTF-8轉換在實踐中幾乎總是成功的,使用異常處理錯誤並不是不合理的。 – Jon

回答

2

一個原則是要考慮如果用戶忽略不知道自己應該檢查你的返回錯誤代碼會發生什麼。

  • 如果代碼理論上可以繼續在錯誤面前,返回一個錯誤可以被認爲是合理的。正如你所提到的,代碼看起來更乾淨。
  • 如果忽略錯誤可能會導致很差的行爲以後,拋出異常可能是一個更好的主意。
  • 第三個潛在的選擇它有點平衡錯誤代碼的簡潔性並強迫程序員意識到潛在的錯誤是讓函數需要引用錯誤代碼。這對於導出的庫以及不能有效處理異常的(大多數是較舊的)編譯器也會有效。

    StringConversionResult result; // Could be a "success" bool

    wstring utf16 = ConvertFromUtf8ToUtf16(utf8, result);

0

如果此功能從庫中,使用返回代碼出口。當使用不同的C/C++運行時庫構建庫和客戶端時,從導出的函數中拋出異常可能會導致程序崩潰。一般來說,這是未定義的行爲。

對於內部使用,我相信異常是一個更好的選擇。你在談論的情況下,當調用者不使用catch塊時,立即崩潰程序(未處理的異常)。這樣做會更好,然後在未來某個時刻繼續執行未定義結果的程序執行。

+0

是的,我知道C++異常不能安全地跨越模塊邊界(除非使用相同的C++編譯器,使用相同的CRT和相同的編譯器設置)。但是這不是問題的焦點:事實上,沿着這種思路,在接口處也使用STL類(如STL字符串)在模塊邊界處不受支持(同樣,除非使用相同的C++編譯器/ CRT /設置)。 –

0

只有三種選擇。首先是「按錯誤代碼點替換所有失敗」 - Unicode標準提供了幾個替換碼點。在某些情況下這很好。第二個是拋出異常。第三個是提供一個錯誤函數對象,在失敗時被調用。例如,

bool fail = false; 
std::u16string str = ConvertFromUTF8ToUTF16(utf8, [&] { 
    return u16"default"; 
    // or 
    throw std::runtime_error("fail"); 
    // or 
    fail = true; 
}); 

的一點是,在任何情況下,你依賴於用戶檢查故障 - 如果他什麼都不做,那麼無論他的功能不下去,編譯器的呼喊,或者它是確定功能繼續。

返回一個錯誤代碼不是一個選項 - 這是很容易出錯的錯誤。