mirror of
https://github.com/ctm/executor.git
synced 2025-02-20 11:28:56 +00:00
139 lines
5.1 KiB
C
139 lines
5.1 KiB
C
#if !defined(__ERROR_CHECK_H__)
|
|
#define __ERROR_CHECK_H__
|
|
/*
|
|
* This file, along with error.c provides wrappers for various OS and
|
|
* toolbox calls. The wrappers automatically check to make sure the
|
|
* routines they wrap are returning noErr. This works even for
|
|
* routines that require calling special error number accessor
|
|
* routines (i.e. Memory and Resource manager routines).
|
|
*
|
|
* In addition to wrapping functions, the "HANDLE_CHECK" macro is
|
|
* provided to make it trivial to check to make sure that a Handle
|
|
* really contains the right number of bytes. This may help in
|
|
* catching Executor bugs and catching where a handle has been purged.
|
|
* If you regularly use handles that contain extra information at the
|
|
* end, you may want to split it up into HANDLE_CHECK_EQ to test for
|
|
* equality and HANDLE_CHECK_GE to test to make sure you have at least
|
|
* enough room.
|
|
*
|
|
* Care has been taken to NEVER evaluate arguments more than once, so
|
|
* you don't have to worry about a wrapper inadvertenly breaking your
|
|
* code because it wraps a routine invocation with a parameter that
|
|
* contains a side-effect. This has been done by using global
|
|
* variables. As such, these routines CAN NOT be used in any code
|
|
* that is asynchronously called, such as ioCompletion, VBL or Time
|
|
* Manager callbacks. Similarly, these routines can't be used in
|
|
* multi-threaded code.
|
|
*/
|
|
|
|
#include "MacTypes.h" /* This should be whatever include files are needed to
|
|
get the type definitions for all the routines we
|
|
wrap in this file */
|
|
|
|
extern int die_on_error; /* whether or not we want to pay attention to errors.
|
|
zero means behave exactly as if we weren't wrapping
|
|
errors. non-zero means we want to log some
|
|
information and then die */
|
|
|
|
extern long int last_error; /* last error a wrapped or checked routine has
|
|
encountered, or noErr if no error was found */
|
|
|
|
extern const char *last_error_file; /* filename of the source file containing
|
|
the trap that saw the last errror */
|
|
|
|
extern int last_error_line; /* line number of the last error */
|
|
|
|
|
|
/* Declarations for each of the wrapper routines we use for monitoring.
|
|
Routines which return anything other than void must also pass in the
|
|
filename and the line number where they were invoked. See error.c for
|
|
a more complete explanation of why this is done. */
|
|
|
|
extern void _DisposHandle (Handle);
|
|
extern Handle _NewHandle (Size s, const char *file, int line);
|
|
|
|
#define badSizeErr (-512) /* the value we log when a size doesn't match */
|
|
|
|
/*
|
|
* All our checking is routed through ERROR_CHECK. We can make it as
|
|
* fancy as we want, but it's important that we only evaluate the first
|
|
* argument once. Since the first thing we do is copy the first argument
|
|
* into last_error, we can still look at that value as many times as we
|
|
* want, as long as it's done via last_error, and not n.
|
|
*/
|
|
|
|
#define ERROR_CHECK(n, file, line) \
|
|
((last_error = (n)) != noErr && die_on_error ? ( \
|
|
last_error_file = file, \
|
|
last_error_line = line, \
|
|
*(long *)-1 = 0) \
|
|
: \
|
|
noErr)
|
|
|
|
/*
|
|
* These defines are for nesting pieces of code where you know you may
|
|
* be invoking routines that will create errors and you want to handle
|
|
* the errors yourself. NOTE: These defines have to be used in pairs,
|
|
* and all the statements between ERROR_CHECK_INHIBIT() and its matching
|
|
* ERROR_CHECK_RESTORE will be part of the same compound statement created
|
|
* by ERROR_CHECK_INHIBIT. If you jump out of this compound statement,
|
|
* die_on_error will be left set to zero, which is bad.
|
|
*/
|
|
|
|
#define ERROR_CHECK_INHIBIT() \
|
|
do \
|
|
{ \
|
|
int save_die_on_error; \
|
|
\
|
|
save_die_on_error = die_on_error; \
|
|
die_on_error = 0
|
|
|
|
#define ERROR_CHECK_RESTORE() \
|
|
die_on_error = save_die_on_error; \
|
|
} \
|
|
while (0)
|
|
|
|
/*
|
|
* The rest of the definitions we will want to use everywhere except
|
|
* when compiling error.c. Not only will our #define of OS and toolbox
|
|
* routines (e.g. NewHandle) get in the way when compiling error.c, the
|
|
* #defines that use __FILE__ and __LINE__ would be misleading if they
|
|
* were used in error.c, since their use would result in an error condition
|
|
* pointing to an error in "error.c", which is not very useful.
|
|
*/
|
|
|
|
#if !defined (__COMPILING_ERROR_C__)
|
|
|
|
#define ERROR_CHECK_INTERNAL(n) ERROR_CHECK(n, __FILE__, __LINE__)
|
|
|
|
#define MEM_CHECK() ERROR_CHECK_INTERNAL(MemError())
|
|
|
|
#define RES_CHECK() ERROR_CHECK_INTERNAL(ResError())
|
|
|
|
#define HANDLE_CHECK(h) \
|
|
do \
|
|
{ \
|
|
Size __s; \
|
|
\
|
|
__s = GetHandleSize (h); \
|
|
ERROR_CHECK_INTERNAL (__s == sizeof **(h) ? noErr : badSizeErr); \
|
|
} \
|
|
while (0)
|
|
|
|
#define DisposHandle(h) \
|
|
do \
|
|
{ \
|
|
_DisposHandle(h); \
|
|
MEM_CHECK(); \
|
|
} \
|
|
while (0)
|
|
|
|
#define NewHandle(s) _NewHandle(s, __FILE__, __LINE__)
|
|
|
|
/* TODO: wrap all the Memory manager, Resource manager calls and any other
|
|
calls whose error returns we may want to check automatically */
|
|
|
|
#endif
|
|
|
|
#endif
|