Show Lecture.Old-fashionedErrorHandling as a slide show.
CS253 Old-fashioned Error Handling
The Old Days
- Before try/recover, throw/catch, and the like became popular,
many other methods of error handling were used.
- Many are still used.
- We’ll look at several old techniques, paying attention to
whether objects get properly destroyed.
- These examples use my
Loud
class,
which announces constructors & destructors
Out-of-band value
- One popular technique is to return an “out-of-band” value
to indicate an error.
- This value must not be one of the normal, non-error, return values.
- For example, a
locate()
function that returns a pointer to
some datum might return nullptr to indicate a failure.
Out-of-band value
Loud a='a';
double invert(double num) {
Loud b('b');
if (num == 0)
return NAN;
return 1/num;
}
int main() {
Loud c('c');
double result = invert(0);
if (isnan(result))
return 1;
cout << result << '\n';
}
Loud::Loud() [c='a']
Loud::Loud() [c='c']
Loud::Loud() [c='b']
Loud::~Loud() [c='b']
Loud::~Loud() [c='c']
Loud::~Loud() [c='a']
- Did all the objects get properly cleaned up?
- NAN is the floating-point Not-A-Number value.
isnan() detects it. Maybe return INFINITY?
That works, but …
- That works. However:
- You need an out-of-band value. This isn’t always possible.
- The caller of
foo()
needs to check for errors, and return early.
- So does his caller.
- So does his caller.
- …
exit
Loud a='a';
double invert(double num) {
Loud b('b');
if (num == 0)
exit(1);
return 1/num;
}
int main() {
Loud c('c');
cout << invert(0) << '\n';
}
Loud::Loud() [c='a']
Loud::Loud() [c='c']
Loud::Loud() [c='b']
Loud::~Loud() [c='a']
- That was brutal.
- Dtor was called for global object, but not for local objects.
abort
Loud a='a';
double invert(double num) {
Loud b('b');
if (num == 0)
abort();
return 1/num;
}
int main() {
Loud c('c');
cout << invert(0) << '\n';
}
Loud::Loud() [c='a']
Loud::Loud() [c='c']
Loud::Loud() [c='b']
SIGABRT: Aborted
- Worse: no dtors called at all.
- Hey, it sent a signal! Like SIGINT (keyboard interrupt),
but it’s SIGABRT (abnormal termation).
assert
Loud a='a';
double invert(double num) {
Loud b('b');
assert(num);
return 1/num;
}
int main() {
Loud c('c');
cout << invert(0) << '\n';
}
Loud::Loud() [c='a']
Loud::Loud() [c='c']
Loud::Loud() [c='b']
a.out: c.cc:5: double invert(double): Assertion `num' failed.
SIGABRT: Aborted
I suppose that we have low expectations for a failed assertion.
Division by Zero
Loud a='a';
static int z = 0;
double invert(double num) {
Loud b('b');
if (num == 0)
z = 1/z;
return 1/num;
}
int main() {
Loud c('c');
cout << invert(0) << '\n';
}
Loud::Loud() [c='a']
Loud::Loud() [c='c']
Loud::Loud() [c='b']
SIGFPE: Floating point exception
- Another new signal: SIGFPE (floating-point exception,
even though it was an integer operation).
- Since it was a signal termination, like abort(), no dtors were called.
The Horror That Is Setjmp/Longjmp
Loud a='a';
jmp_buf env;
double invert(double num) {
Loud b('b');
if (num == 0)
longjmp(env, 456);
return 1/num;
}
int main() {
Loud c('c');
if (int val = setjmp(env)) {
cout << "error=" << val << "!\n";
return 1;
}
cout << invert(0) << '\n';
}
Loud::Loud() [c='a']
Loud::Loud() [c='c']
Loud::Loud() [c='b']
error=456!
Loud::~Loud() [c='c']
Loud::~Loud() [c='a']
- Lord, take me now—I done seen everything!