In writing a function to convert between strings of different encodings (e.g. from UTF-8 to UTF-16), what would be the best way to handle errors (e.g. invalid input UTF-8 byte sequence)? Throwing an exception or returning an error code (even a 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);
Using exceptions, it would be possible to do chained function calls (when the function return value is used as input for other functions/methods).
But I’m not sure that using exceptions in this case is good; I was thinking of what Eric Lippert in his quality blog post calls vexing exceptions (and related Int32.Parse()/TryParse() example).
For example, if exceptions are used, the caller should be forced to wrap the function call in try/catch blocks to check the case of invalid UTF-8 input:
try
{
wstring utf16 = ConvertFromUtf8ToUtf16(utf8);
}
catch(const Utf8ConversionException& e)
{
// Bad UTF-8 byte sequence
...
}
Which seems not ideal to me.
Maybe the best thing to do is to just provide both overloads (implementing the conversion code in the non-throwing overload, and in the throwing overload just call the non-throwing version, and in case of error return code throw an exception)?
One guideline is to consider what will happen if users ignore or don’t know that they should check your returned error code.
A third potential choice which somewhat balances the terseness of error codes and forcing the programmer to be aware of potential errors is to make the function require a reference to the error code. This will also work well in exported libraries and with (mostly older) compilers that don’t handle exceptions efficiently.
StringConversionResult result; // Could be a "success" boolwstring utf16 = ConvertFromUtf8ToUtf16(utf8, result);