written by jahlborn@parc.xerox.com
(please direct questions and comments here)
ErrCode foo(...args...) { DGP_PROLOGUE("foo()", <perr_state>); <-set the error state variable for the function...body...NORMAL_RETURN; <- return with no errors// possibly with this part, if cleanup is neededERR_CLEANUP: <- optional block of code to jump to in case of errors...cleanup code...ERR_RETURN; <- return an error }Notes:
DGP_PROLOGUE()
sets the function name and error state cookie for the error macros. The <perr_state> should be of type(DgpErrState *)
. If you are using a(DgpState *)
nameddgp_state
to pass the error state around, you can access the error cookie using the macrodgp_pErrState
(defined to dgp_state->err_state). This value can be set to NULL and error output will be suppressed.NORMAL_RETURN
can be used anywhere you would use return with no error, andERR_RETURN
returns with an error.ERR_CLEANUP:
must be defined if you want to use any*_CLEANUP*()
macros.Example:
(two functions:
foo()
which callsmy_funct()
, both return errors):
#include "dgp_err_control.h"ErrCode my_funct(..., DgpState *dgp_state) { DGP_PROLOGUE("my_funct()", dgp_pErrState); <-set the error state to whatever is in the dgp_state void *array = NULL;... DGP_CHECK_ALLOC_CLEANUP(array = dgp_Malloc(this_size)); <-error checked memory allocation (using dgp allocators) ...NORMAL_RETURN; <-return if nothing failsERR_CLEANUP: <-jump here on errors... if(array != NULL) dgp_Free(array); <-free the array we allocated (using dgp allocators)ERR_RETURN; <-return an error }ErrCode foo(..., DgpState *dgp_state) { DGP_PROLOGUE("foo", dgp_pErrState); <-set the error state to whatever is in the dgp_state... DGP_ERR_PASS(my_funct(..., dgp_state)); <-error checked function call ...NORMAL_RETURN; <-normal return if no errors }
int main(int argc, char **argv) { DGP_MAIN_PROLOGUE("main()", <perr_cookie>, <perr_printer>); <-set error printer and cookie...body...MAIN_NORMAL_RETURN; <-return EXIT_SUCCESS// possibly with this part, if cleanup is neededERR_CLEANUP: <-jump here on errors...cleanup code...MAIN_ERR_RETURN; <-return EXIT_FAILURE }Notes:
- <perr_printer> is the error printer function (default ones defined in
dgp_err_printers.h
)- <perr_cookie> is the user defined cookie you want passed into the error printer function.
- If you set up a
dgp_state
variable using a call toDgp_InitDgpState()
, you probably want to use the macro
DGP_SET_ERR_STATE(dgp_pErrState);
to update the error state pointer in the main function.
Example:
#include "dgp_err_control.h" #include "dgp_err_printers.h"int main(int argc, char **argv) { DGP_MAIN_PROLOGUE("main()", stderr, Dgp_FileErrPrinter); <-use stderr for error output now. DgpState *dgp_state = NULL;... DGP_ERR_PASS_CLEANUP(Dgp_InitDgpState(&dgp_state, ..., my_err_cookie, my_err_printer,...)); ^-error checked function call to create a digipaper state. DGP_SET_ERR_STATE(dgp_pErrState); <-change current error output to my_err_cookie and my_err_printer ......main body...... Dgp_CleanupDgpState(&dgp_state); <-cleanup dgp stateMAIN_NORMAL_RETURN; <-return EXIT_SUCCESS// possibly with this part, if cleanup is neededERR_CLEANUP: <-jump here on errors... Dgp_CleanupDgpState(&dgp_state); <-cleanup dgp state in case of errors ...MAIN_ERR_RETURN; <-return EXIT_FAILURE }Notes:
- The example makes use of the "standard" file error output printer. See section 4. Error printers below.
There are many macros to handle errors in
dgp_err_control.h
,dgp_xif_err_control.h
,dgp_sys_err_control.h
, anddgp_w32_err_control.h
. The first part will be the common, general use macros (dgp_err_control.h
), and the second part will give more specifics on the other files.
- General Reports:
DGP_REPORTF0((do_report), "my report");
-prints the report string iff(do_report)
evaluates to boolean true.- Warnings:
DGP_WARNF0("my warning");
-prints a warning to the user, does not throw and error.
- Asserts:
DGP_ASSERT(my_condition);
-prints an assert failure notice and then exits the program iff my_condition is boolean false.
- Panic:
DGP_PANIC("my explanantion");
-prints the error string then exits the program.
- Throwing an error:
DGP_ERR(err_code);
-throws the given
err_code
.- Conditionally throwing an error:
DGP_ERR_GEN((my_cond), err_code);
-throws the given
err_code
iffmy_cond
is boolean true.- Handling a function error return:
DGP_ERR_PASS(my_funct(...));
-throws the error thrown by the function iff the error code is NOT
cErrSuccessful
(it "passes" the error up the stack). -this macro acts differently in the non-debug case in that it does not print an error. so, in non-debug code, you will only get output from the original error (all that's really useful to an end user anyway).DGP_ERR_CHECK(my_funct(...)) { }
-gives the user a chance to handle the error returned iff the error code is NOT
cErrSuccessful
. (needs to be followed by a block of code).- Memory allocation:
DGP_CHECK_ALLOC(ptr = (ptr type)malloc(sizeof(*ptr)));
-throws a cErrNomem error when the returned value is
NULL
.- General NULL ptr checks:
DGP_CHECK_NULL(call);
-throws a cErrNullPtr error iff
call == NULL
.DGP_CHECK_NOT_NULL(call)
-throws a cErrPtrNotNull error iff
call
!=NULL
.- Testing the current error code:
DGP_ERROR_IS(err_code)
-returns boolean true iff the current
error == err_code
, boolean false otherwise.DGP_ERROR_IS_NOT(err_code)
-pretty darn self-explanatory if you ask me...
- Variants:
- Many of the above macros have variants such as:
..._CLEANUP
- jump to
ERR_CLEANUP
before returning an error....Fn
- (F0, F1, ...)
- allows formatted strings to follow with n number of arguments, e.g. ...F2(..., "my string %d %d", i, j);..._NO_MSG
- do not output anything, but otherwise do whatever this macro is supposed to do.
- various combinations:
_CLEANUPFn
,_CLEANUP_NO_MSG
...Other macros:
There are other macros to do more complicated or fine grained things, you'll have to check them out for yourself. You can do pretty much whatever you need to if you find the right macros. (ask jahlborn@parc.xerox.com for help if you need it). HOWEVER, please try to use the defined macros, not the actual variables themselves. If you use the variables themselves and changes are made, your code goes kaput... bad. If you use the macros, chances are, the functionality will remain the same, and your code will continue to build... mmmmm, good.
Specialized error handler macros:
dgp_xif_err_control.h
-Has macros for handling errors thrown from
XIF_...()
andwp_Xif...()
functions. The XIF specific errors overlap with ipcore error codes, so they need to be mapped to a non-overlapping set. Use these macros and everything is peachy!dgp_sys_err_control.h
-Has macros for testing returns for c lib function calls (such as
fopen()
, etc.). These functions use theerrno
variable to return the error code, so these macros make use of that to retrieve the error code and map it to a non-overlapping set.dgp_w32_err_control.h
-Has macros for testing returns from win32 function calls (such as
CreateFile()
, etc.). These functions useGetLastError()
to retrieve the error code and map it to a non-overlapping set.
liberr has some default error handlers built into it, or you can write your own. The default handlers are (you must include dgp_err_printers.h):
Dgp_FileErrPrinter()
- your error printer in the
DGP_MAIN_PROLOGUE
should beDgp_FileErrPrinter
.
- your error cookie in theDGP_MAIN_PROLOGUE
should be a FILE * (such as stderr).
- errors, reports, and warnings will be printed to the given file.Dgp_StrErrPrinter()
- your error printer in the
DGP_MAIN_PROLOGUE
should beDgp_StrErrPrinter
.
- your error cookie in theDGP_MAIN_PROLOGUE
should be achar[]
(hopefully long enough so that no error messages overrun it).
- errors, reports, and warnings will be printed to the string (each error will over write the old one).Dgp_SafeStrErrPrinter()
- your error printer in the
DGP_MAIN_PROLOGUE
should beDgp_SafeStrErrPrinter
.
- your error cookie in theDGP_MAIN_PROLOGUE
should be a(dgpSafeErrString *
) (this is defined in the same file, along with create and delete macros). This will not overrun its buffer.
- errors, reports, and warnings will be printed to the string (each error will over write the old one).Notes for defining your own error handler:
- Warnings and reports have special error codes, check these out in
dgp_err_control.h
.- You must write separate functions for the debug and non-debug compilation modes (or at least separate function headers). Check out
dgp_err_printers.h
anddgp_err_printers.c
for examples.- Use your new error printer function and your error cookie in the
DGP_MAIN_PROLOGUE
macro and/orDgp_InitDgpState()
function call.
The error codes we use are mainly from ipcore (
ipwerrs.pub
,ipserrs.pub
) with some of our own defined indgp_errs.h
.New Error Codes:
- Update
dgp_errs.h
(say the next free error code is(cDgpGen + 10)
)
... #define cErrFoo cDgpGen + 10 /* My foo error for when things go foo!*/ <-your new error code! ...
- Update
dgp_err_control.c
(basically update the number of errors, add a comma after the last error string, and add your error string)
... #define NUM_DGP_ERRORS 11 <-updated from its former value of 10 ... char *DGP_ERRORS[NUM_DGP_ERRORS] = { ... "The old last error string", // cErrPrev cDgpGen + 9 <-former last error string (add comma) "My new error string foo" // cErrFoo cDgpGen + 10 <-new last error string ... }
Assertions in digipaper work much like normal assertions, but with a few small differences:
DGP_ASSERT(my_test)
:
- Returns a
cErrAssertFailure
to the calling function iffmy_test
!= boolean true.- The use of this requires a
DGP_PROLOGUE()
in the function, and the function itself must return anErrCode
. If this is not the case, **VERY CONFUSING** things could happen. Why you ask? BecauseDGP_ASSERT
differs from a normal assert in that it does not exit the program immediately, it instead returns acErrAssertFailure
error code to the calling function. This enables the tester to see the function trace of the error location, which greatly helps debugging. If the function returns something other than anErrCode
, the calling function could be very confused by the return code.DGP_VOID_ASSERT(my_test)
:
- Exits the program immediately iff
my_test
!= boolean true. (more similar to a normal assert)- The use of this requires a
DGP_VOID_PROLOGUE()
in the function, so that the assert can possibly print some output if there is error printing enabled in the prologue. This can not only be used in functions which have no return value, but also functions which return something other than anErrCode
.
$Header: /project/icons/cvsroot/digipaper/doc/Error\040Handling.htm,v 1.3 2000/04/28 21:39:49 jahlborn Exp $