Errors
Introduction
Classification of errors: - Compile-time errors violate language rules and are found by the compiler. - Link-time errors are found by the linker when it is trying to combine object files into an executable program. - Run-time errors are found by checks in a running program, which could be detected by - Computer hardware or OS - Library - User code - Logic errors are found by the programmer after analysis of wrong results.
We assume that the program we write: 1. Should produce results for all legal inputs. 2. Give reasonable error messages for all illigal inputs. 3. Need not worry about falty hardware. 4. Need not worry about errors in system software. 5. Is allowed to terminate after finding an error.
There are 3 approaches for writing code that should be dealt with at once: 1. Organize software. 2. Eliminate errors through debugging and testing. 3. Make sure the remaining errors are not serious.
Sources of errors
- Poor specification (be specific what a program should do);
- Incomplete programs (handle all cases);
- Unexpected arguments (check all arguments passed to functions);
- Unexpected input (handle user input);
- Unexpected state (handle incomplete or wrong data);
- Logical errors (programs should do what they are supposed to do).
Compile-time errors
Syntax errors are not properly formed lines of code according to C++ grammar rules. Syntax error messages may be difficult to understand because the compiler might go further the actual error to make sure it is definitely an error.
int s1 = area(7; // missing )
int s2 = area(7) // missing ;
Int s3 = area(7); // wrong type Int
int s4 = area('7); // missing closing '
Type errors are the mismatches between the types declared and types assigned.
int x0 = arena(7); // undeclared function
int x1 = area(7); // wrong number of arguments
int x2 = area("seven",2); // first argument has wrong type
Link-time errors
A program is made of various separately compiled parts, called translation units. Any function or variable must be defined only once and declared with the same type in every translation unit, otherwise the linker gives an error.
int area(int length, int width);
int main() {
int x = area(2,3); // undefined reference to area
}
Run-time errors
When dealing with run-time errors, there are two approaches: either to deal with errors by a caller to the function, or let the callee (the called function) deal with errors. The best approach is to let the callee deal with errors, because the argument checking code is in one place.
int area(int length, int width) {
// calculate area of a rectangle
if (length <= 0 || width <= 0) error("non-positive area() argument");
return length*width;
}
However, there are reasons not to deal with errors by the callee when: 1. We can't modify the function definition. 2. The called function doesn't know what to do in case of error. 3. The called function doesn't know where it was called from. 4. High-performance code is involved.
Also, error reporting may involve returning an error value (e.g. -1 for area) instead of error message. However, this approach cannot be used in many cases because: 1. Now both the caller and the callee must test the arguments. 2. A caller can forget to test. 3. Many functions do not have an extra return value to report an error.
Exceptions
Exceptions help to deal with errors. The idea behind exceptions is to separate detection of an error (done in callee) from handling of an error (done in caller) while making sure the error is not ignored.
If a function encounters an error, it throw
s an exception
saying what went wrong. The caller can catch
the exception, specifying how to handle a particular exception using a try
block. If no caller catches the exception, the program terminates with a default system error (an uncaught exception error).
// a type for reporting errors
class Bad_area{};
// calculate area of a rectangle
int area(int length, int width) {
if (length <= 0 || width <= 0) throw Bad_area();
return length*width;
}
The value returned from main()
is used by the OS. A zero indicates successful completion, and a nonzero indicates that something went wrong.
int(main) {
try {
// our program
return 0; // 0 for successful completion
}
catch (exception& e) {
cerr << "error: " << e.what() << '\n';
return 1; // 1 indicates failure
}
catch (...) {
cerr << "Oops: unknown exception!\n";
return 2; // 2 indicates failure
}
}
When displaying an error, we are interested in two pieces of information describing the error: the type of error (runtime_error
or out_of_range
) and the error message (explaining the encountered error in more detail).
There are various exceptions to be caught, such as:
1. Bad argument (negative value for length);
2. Range error as a special case of bad argument error (off-by-one error when trying to read a Vector
container element);
3. Bad input (a letter input from the user instead of a number);
4. Narrowing errors (casting floating-point numbers to integers).
Source: Stroustrup, Bjarne Programming: Principles and Practice Using C++.Addison-Wesley, 2008.