The first is the use of checkpoints. These are regularly spaced points in your code where you store a unique code number in a static variable to keep track of where you are in the execution of your own code.
The second idea is to channel your response to all the errors you catch by various methods through a single code point. The reason is that with a single break point you can stop your program in time to trace the execution back to where the error was generated.
The third idea is to pass an origin code to subroutines which you make a large number of calls from numerous locations in your code. This is to help you immediately identify the location from which the subroutine was called. If you make a large enough number of calls to this routine, tracing back to exactly which call to it led to the error can be difficult.
It is a good idea to make this error code a seperate module, since it is something you can use in all of your projects. In this module you can make the header file something to include by other modules. In addition to your static checkpoint variable, you can define a macro which stores the checkpoint code and tests for errors. When an error is found you call the subroutine (error_break) which handles all of your errors according to the second idea above. Another useful item is a flag which tells your program whether to terminate on an error (user mode) or continue execution (debugging mode) for the purpose of tracing and error back to its source.
error.h
CODE
//Use this macro to check for errors while recording a checkpoint location
//test represents any additional local conditions under which to signal an error
#define checkpoint(test,j); {checkpointcode=j;if(test||errno==EDOM)error_break(j);}
extern bool exit_on_error;
extern int checkpoint;
void error_break(int j);
//you will also want to define any special exception types you might need here
//When you throw an exception you can throw anything at all: any integer, array, class, etc. in your program.
//So defining a seperate structure like this for an exception is optional. However using a different structure
// for each type of exception makes your programming more readable and managable.
struct specialException {
int i,j,k;};
struct error_break_Exception {
char buf[100];};
//test represents any additional local conditions under which to signal an error
#define checkpoint(test,j); {checkpointcode=j;if(test||errno==EDOM)error_break(j);}
extern bool exit_on_error;
extern int checkpoint;
void error_break(int j);
//you will also want to define any special exception types you might need here
//When you throw an exception you can throw anything at all: any integer, array, class, etc. in your program.
//So defining a seperate structure like this for an exception is optional. However using a different structure
// for each type of exception makes your programming more readable and managable.
struct specialException {
int i,j,k;};
struct error_break_Exception {
char buf[100];};
Now you can put the actual variables and subroutine code in the c++ file. You can also put in a _matherr routine to overrule the default handler.
error.cpp
CODE
// exit_on_error set to false causes the program to continue execution after an error
// use with breaks on the line below to trace the errors back to the source
bool exit_on_error=false; //change this to true when you are finished debugging your program
int checkpointcode=0;
//Since this file does not call error.h you need define here any exception types you use in this file
struct error_break_Exception {
int j;};
//---------------------------------------------------------------------------
void error_break(int j){
errbrkException e;
e.j=j;
//################################################################################
###
//******************put a break on the next line to cause program to halt when called
//################################################################################
###
errno=0;
//here you can display error codes
// store a whole list of error codes
// throw an error_break_Exception under whatever conditions, for example
if(j>27){e.j=j;throw e;}
return;
}
//---------------------------------------------------------------------------
int _matherr (struct _exception *a){
// In some case you may want to put code here to modifiy the return value without generating an error
// For example acos and asin generate errors outside the range of -1 to 1. Since it is easy to fall
// outside of this range due to round off error you may simply want to return an appropriate value
// rather than generate any kind of error. This is accomplished by the following code.
if(!strcmp(a->name,"acos")){
if(a->arg1>1.0&&a->arg1<1.01){a->retval = 0.0;return 1;}
if(a->arg1<-1.0&&a->arg1>-1.01){a->retval = M_PI;return 1;}}
if(!strcmp(a->name,"asin")){
if(a->arg1>1.0&&a->arg1<1.01){a->retval = M_PI_2;return 1;}
if(a->arg1<-1.0&&a->arg1>-1.01){a->retval = - M_PI_2;return 1;}}
error_break(9999);
//when you return from error_break you can examine the type, name and arguments of the error.
//...........................................................................
//a->type == DOMAIN Argument was not in domain of function, such as log(-1).
//a->type == SING Argument would result in a singularity, such as pow(0, -2).
//a->type == OVERFLOW Argument would produce a function result greater than DBL_MAX (or LDBL_MAX), such as exp(1000).
//a->type == UNDERFLOW Argument would produce a function result less than DBL_MIN (or LDBL_MIN), such as exp(-1000).
//a->type == TLOSS Argument would produce function result with total loss of significant digits, such as sin(10e70).
// You can check which math function generated the error by looking at a->name
// You can check the arguments of the math function by looking at a->arg1 and a->arg2 (if 2 arguments)
//...........................................................................
if(exit_on_error)return 0;
return 1;
}
// use with breaks on the line below to trace the errors back to the source
bool exit_on_error=false; //change this to true when you are finished debugging your program
int checkpointcode=0;
//Since this file does not call error.h you need define here any exception types you use in this file
struct error_break_Exception {
int j;};
//---------------------------------------------------------------------------
void error_break(int j){
errbrkException e;
e.j=j;
//################################################################################
###
//******************put a break on the next line to cause program to halt when called
//################################################################################
###
errno=0;
//here you can display error codes
// store a whole list of error codes
// throw an error_break_Exception under whatever conditions, for example
if(j>27){e.j=j;throw e;}
return;
}
//---------------------------------------------------------------------------
int _matherr (struct _exception *a){
// In some case you may want to put code here to modifiy the return value without generating an error
// For example acos and asin generate errors outside the range of -1 to 1. Since it is easy to fall
// outside of this range due to round off error you may simply want to return an appropriate value
// rather than generate any kind of error. This is accomplished by the following code.
if(!strcmp(a->name,"acos")){
if(a->arg1>1.0&&a->arg1<1.01){a->retval = 0.0;return 1;}
if(a->arg1<-1.0&&a->arg1>-1.01){a->retval = M_PI;return 1;}}
if(!strcmp(a->name,"asin")){
if(a->arg1>1.0&&a->arg1<1.01){a->retval = M_PI_2;return 1;}
if(a->arg1<-1.0&&a->arg1>-1.01){a->retval = - M_PI_2;return 1;}}
error_break(9999);
//when you return from error_break you can examine the type, name and arguments of the error.
//...........................................................................
//a->type == DOMAIN Argument was not in domain of function, such as log(-1).
//a->type == SING Argument would result in a singularity, such as pow(0, -2).
//a->type == OVERFLOW Argument would produce a function result greater than DBL_MAX (or LDBL_MAX), such as exp(1000).
//a->type == UNDERFLOW Argument would produce a function result less than DBL_MIN (or LDBL_MIN), such as exp(-1000).
//a->type == TLOSS Argument would produce function result with total loss of significant digits, such as sin(10e70).
// You can check which math function generated the error by looking at a->name
// You can check the arguments of the math function by looking at a->arg1 and a->arg2 (if 2 arguments)
//...........................................................................
if(exit_on_error)return 0;
return 1;
}
In your main program you will use one or more try catch statement sets to catch error exceptions.
CODE
#include "error.h"
...
//Using more than one try - catch set will also help you to identify what part of your code generated the error
try {//your code, usually calls to subroutines and functions in your other modules
}
catch (error_break_Exception &e) {
<code to display error message or information contained in e>
//since this exception is generated by error_break itself, you do not call error break here.
if(exit_on_error)exit_program(0);}
catch (Exception &ex) {
<code to display error message or information contained in ex>
if(exit_on_error)exit_program(0);
else errbrk(0);}
catch (...) {
//here is where you catch unknown exceptions types that may be thrown in libraries you use
//You have no idea what informations was sent in the exception so you cannot display that
// But you can display the checkpoint code that was saved at your last checkpoint.
<code to display error message with last checkpoint code>
if(exit_on_error)exit_program(0);
else errbrk(0);}
...
//Using more than one try - catch set will also help you to identify what part of your code generated the error
try {//your code, usually calls to subroutines and functions in your other modules
}
catch (error_break_Exception &e) {
<code to display error message or information contained in e>
//since this exception is generated by error_break itself, you do not call error break here.
if(exit_on_error)exit_program(0);}
catch (Exception &ex) {
<code to display error message or information contained in ex>
if(exit_on_error)exit_program(0);
else errbrk(0);}
catch (...) {
//here is where you catch unknown exceptions types that may be thrown in libraries you use
//You have no idea what informations was sent in the exception so you cannot display that
// But you can display the checkpoint code that was saved at your last checkpoint.
<code to display error message with last checkpoint code>
if(exit_on_error)exit_program(0);
else errbrk(0);}
Here is another method for catching errors defined in signal.h, which I have put in my error handling code.
Of course you need this at the top of your program.
CODE
#include <signal.h>
Then you need to signal to install the signal handler at the beginning of you main program
CODE
signal(SIGFPE, (fptr)SignalSIGFPECatcher);
Add your signal handler prototype to error.h
CODE
void SignalSIGFPECatcher(int *reglist);
Then you need to add your signal handler to error.cpp
CODE
//---------------------------------------------------------------------------
typedef void (*fptr)(int);
void SignalSIGFPECatcher(int *reglist){
signal(SIGFPE, (fptr)SignalSIGFPECatcher); // ******reinstall signal handler
error_break(9998);
if(exit_on_error)_exit(1);
else *(reglist + 8) = 3; /* make return AX = 3 */
}
typedef void (*fptr)(int);
void SignalSIGFPECatcher(int *reglist){
signal(SIGFPE, (fptr)SignalSIGFPECatcher); // ******reinstall signal handler
error_break(9998);
if(exit_on_error)_exit(1);
else *(reglist + 8) = 3; /* make return AX = 3 */
}
The signal types that you can catch this way are as follows:
...........................................................................
SIGABRT Abnormal termination (default handler calls _exit(3))
SIGFPE Bad floating-point operation (default handler calls _exit(1))
Arithmetic error caused by
division by 0, invalid operation, etc.
SIGILL Illegal operation (default handler calls _exit(1))
SIGINT Control-C interrupt (default handler calls _exit(3))
SIGSEGV Invalid access to storage (default handler calls _exit(1))
SIGTERM Request for program termination (default handler calls _exit(1))
...........................................................................

