字符串转换错误:异常或错误代码?

问题描述:

在编写不同编码的字符串(例如从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://*.com/questions/6300804/wchars-encodings-standards-and-portability)。 –

+2

假设从UTF-8转换在实践中几乎总是成功的,使用异常处理错误并不是不合理的。 – Jon

一个原则是要考虑如果用户忽略不知道自己应该检查你的返回错误代码会发生什么。

  • 如果代码理论上可以继续在错误面前,返回一个错误可以被认为是合理的。正如你所提到的,代码看起来更干净。
  • 如果忽略错误可能会导致很差的行为以后,抛出异常可能是一个更好的主意。
  • 第三个潜在的选择它有点平衡错误代码的简洁性并强迫程序员意识到潜在的错误是让函数需要引用错误代码。这对于导出的库以及不能有效处理异常的(大多数是较旧的)编译器也会有效。

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

    wstring utf16 = ConvertFromUtf8ToUtf16(utf8, result);

如果此功能从库中,使用返回代码出口。当使用不同的C/C++运行时库构建库和客户端时,从导出的函数中抛出异常可能会导致程序崩溃。一般来说,这是未定义的行为。

对于内部使用,我相信异常是一个更好的选择。你在谈论的情况下,当调用者不使用catch块时,立即崩溃程序(未处理的异常)。这样做会更好,然后在未来某个时刻继续执行未定义结果的程序执行。

+0

是的,我知道C++异常不能安全地跨越模块边界(除非使用相同的C++编译器,使用相同的CRT和相同的编译器设置)。但是这不是问题的焦点:事实上,沿着这种思路,在接口处也使用STL类(如STL字符串)在模块边界处不受支持(同样,除非使用相同的C++编译器/ CRT /设置)。 –

只有三种选择。首先是“按错误代码点替换所有失败” - Unicode标准提供了几个替换码点。在某些情况下这很好。第二个是抛出异常。第三个是提供一个错误函数对象,在失败时被调用。例如,

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

的一点是,在任何情况下,你依赖于用户检查故障 - 如果他什么都不做,那么无论他的功能不下去,编译器的呼喊,或者它是确定功能继续。

返回一个错误代码不是一个选项 - 这是很容易出错的错误。