FYI: I was asked to post this on softwareengineering - this question was originally posted on stack overflow, but they are a bit funny about broad questions over there, hence I was told to post here instead...
I have searched stackoverflow for relevant questions and was surprised to find that I couldn't find any questions tagged with C++ with in depth discussions of best practices for error handling in C++.
I found this suprising for a number of reasons:
There are other questions which are quite famous within the community on similar subject areas. Notable examples include the in-depth discussions of how to implement the many different operators in user-defined C++ classes. (Including mathematical operators such as operator+
as well as io operators such as operator<<
... I'm sure many of those reading will recall which question and answer I refer to here.)
There appear to be lots of possible choices to make when writing error handling code, just as there is when writing operator implementations. However, just as with operators, there is probably only one "sensible" implementation. For example, one could implement a matrix multiply using the operator<<
, but this would not be the expected behaviour for this function and so it makes little logical sense to make such a decision. Similarly with errors, one can "throw" just about anything, including just std::string
objects implicitly constructed from const char*
's.
Intro: C style errors, reasons not to use them in C++
I know roughly how I can implement errors and error handling code in C++, but I am not confident that I know how to do this in a way which is approved of by industry. I would like to correct my ways by asking this question.
The C style of error handling is simple: One writes functions which return int
and then one defines error codes which can be returned.
There are several problems with this:
- Error codes have to be global variables, and are probably defined in some obscure file hidden in the source code folders somewhere.
- Error codes have to be (should be) unique. It is difficult to maintain a list of unique codes unless they are all in the same file. If one is writing an application with several independent components, it makes little to no sense to define all the error codes for two seperate components in the same file.
- If one splits the error codes into two or more files there may be conflicts and clashes causing nonsensical code to compile ok.
Here is an example, consider a math module and a network module.
const int ERROR_CODE_MATH_DOMAIN_ERROR = 100;
const int ERROR_CODE_NETWORK_IP_UNREACHABLE = 100;
int math_function(argument)
{
return ERROR_CODE_MATH_DOMAIN_ERROR;
}
int main()
{
if(math_function(arguments) == ERROR_CODE_NETWORK_IP_UNREACHABLE)
// works, and compiles, and detects the right kind of error
// but makes literally zero sense to the reader
}
The other advantage of C++ errors using try-catch is that error flow is seperated from normal no-error program flow.
Given the above, it would be better to use try-catch
symantics to handle errors.
Often when programming I am quite lazy at dealing with errors, so I often find myself defaulting to the C-style-return-an-error-code paradigm.
Question: Should I just throw a string?
I am aware that one can do this:
void error_throwing_function()
{
if(true)
{
throw "an error has certainly occured";
}
}
However, some questions arrise:
- Is this good code?
- What kind of error does this throw? A
std::string
object?
- How do I catch this kind of error without catching all errors?
- Is this considered bad practice?
To explain the 3rd bullet in more detail, to catch this error one may do something like this:
try
{
error_throwing_function()
}
catch (... what goes here? ...)
// catch(std::string) // does this work? does it make sense?
{
// only option is to catch "all possible errors" - maybe?
}
Question: Is it better to implement my own classes?
Is it best practice to implement my own classes for types of error which may occur in my library? (Using inheritance.)
The C++ standard library defines a number of default errors. These can be found here.
All possible C++ error inherit from std::exception
... This raises a new question linked to the previous section:
Further, the STL defines a set of default errors which are used in STL. Notable examples include the std::out_of_range
error type which is thrown by the STL containers when something like at()
is called and the argument references a section of memory which is out of bounds. Link
When implementing a new library, should one make used of the STL errors? Is it good olr bad practice to do so? Why?
Or should one define new classes inheriting from std::exception
to implement new types of error which related to a new libary implementation?
Should these error types always inherit from the base class std::exception
or should they sometimes inherit from another (derived) STL error type such as std::out_of_range
?
Question: Any other notable best practices?
Always good to be aware of as much information as possible so is there anything I have missed so far?
This question doesn't particularly relate to any version of C++, but since most of us are writing C++11 or later code, those standards are likely of particular interest.