Generic Error Object
The architecture description defines the interface
and functionality for a generic error object. The intent is to create an error
object that behaves as a bit-field, but allowing more than 32 bits of error
flags. Normally, this would require defining several words to hold the error
flags, and each error word would have to be checked. If the programmer forgets
to check every error word, then the software may ignore certain types of
errors. This object prevents that type of defect by encapsulating the error flags
and error checking into an object.
To expand to more than 32 error flags requires using
several DWORDs and a method of choosing which DWORD is accessed. Had the
programmer been required to scroll through error groups then the original
reason for this object would be defeated the purpose is to isolate the
programmer from the implementation of the error flags and to make the error
code uniquely identifiable with a single DWORD. Since a DWORD is 32 bits, some
form of structure or array is required to have more than 32 error flags.
This object creates four groups of error flags, each
group with 30 bit flags. Errors can be checked by specific error, by group, or
by all groups. This gives a total of 120 error flags that can be defined by
this object.
The 32 bits in the error flag will be defined as 30
error bits and 2 address bits. The address bits identify which DWORD will be
used. Errors are separated into groups and can be checked by groups or all
together.
In addition to the standard C and Windows data
types, the following classes and data types will be defined by the object and
available to anyone using the object.
|
Class
Name |
Usage |
|
CErrorObject |
Class
that implements the API in this document. |
|
Data
Type |
Usage |
|
ERRORTYPE |
DWORD.
Defines a variable that is an error code. |
The following constants will be defined by the
object and available to anyone using the object.
|
Data
Type |
Usage |
|
NOERRORFLAGS |
DWORD.
Means that no error exists. Defined as 0L. |
|
GROUP1 |
DWORD.
Identifies the first error group. 0x0 |
|
GROUP2 |
DWORD.
Identifies the second error group. 0x1 |
|
GROUP3 |
DWORD.
Identifies the third error group. 0x2 |
|
GROUP4 |
DWORD.
Identifies the fourth error group. 0x3 |
|
ERRORFLAG1
through |
DWORD.
Bit fields to define error flags 1 through 30. Oring ERRORFLAGx with GROUPx
makes a complete ERRORTYPE value. 0x00000004 through 0x80000000 |
|
ALLGROUP1
through |
DWORD.
0xFFFFFFFC combined with the GROUP1 through GROUP4 flags. This allows all
flags in the group to be set, cleared, or checked at once. |
Error codes are defined by using the two LSBs as the GROUPx flags and the 30 upper bits as the 30 error flags. A complete error code is then created by combining the group with the error flag. The general form is:
Error Code = Error Flag | Group.
The error object can extract the group from the error code and use it to select the appropriate error DWORD. An application using this object should define constants and then always using the constants. For example:
#define UPPERLIMITERROR (ERRORFLAG2 | GROUP2)
This object is designed to allow multiple errors to
be set, checked, or cleared at once. That dictates an error flag arrangement
such as 1, 2, 4, 8, 16; rather than a simple series of error codes such a 1, 2,
3, 4, 5, etc. Had a simple series been used, then only one error could be set
or checked at a time, and a larger data structure would have been needed to
hold multiple errors.
The error flags are intended to be combined with the
group flags to make a complete 32 bit error code. This means that completed
error codes can only be ORed with error codes in the same group.
ERRORFLAG1 through 30 must each have one and only
one bit set. This makes each error flag unique and combinable with other error
flags.
No more than 120 error codes can be handled with this object.
One possible objection is that it still relies on the client knowing something of the internal implementation - the 30 error flags and the 2 group flags. However, that is not the case. This format was dictated by design and not the internal implementation. Instead, the object was coded to accept this format. A more isolated method would have been to have enums for errors and store them in a linked list. However, the application for which this was designed often had to check to see if several specific errors had occurred, while ignoring other errors. Using an enum would have generated a series of "if (error A occurred)...if (error B occurred)...if (error C occurred)..." ad infinitum. In contrast, sticking with the "C" style of or-ing bit flags allowed a group of errors to be created, so the code could check "if (any algorithm error occurred)..."
void CErrorObject(CWnd*
hDestWin = NULL, WPARAM wMsg = 0)
Constructor for error object. Can pass in a
window handle to receive a message each time an error is set. If an hDestWin is
specified, then wMsg will be sent using SendMessage( ) each time the function
SetError( ) is called. This can be used to notify the user interface that an
error has occurred.
hDestWin Window
to receive the message.
wMsg Message
to send. Should be based on WM_USER + n or WM_APP + n. The windows message loop
should include an ON_MESSAGE( wMsg, xFn ) statement to process the message. The
prototype for xFn is:
void xFn(WPARAM wParm = 0, LPARAM lParm = 0).
The wParm and lParm are not used, but must be declared because SendMessage( )
will push them onto the stack..
RETURNS
void.
void SetError(ERRORTYPE
eType)
EType Error
code to set. Multiple error codes within the same group can be combined to set
several errors at once. If eType = ALLGROUPx then all errors in that group are
set.
If the constructor specified an hDestWin, then the message wMsg will be sent
using SendMessage( ).
RETURNS
void.
void ClearError(ERRORTYPE
eType)
EType Error
code to clear. Multiple error codes within the same group can be combined to
clear several errors at once. If eType = ALLGROUPx then all errors in that
group are cleared. This does NOT clear any error text strings (see the error
text section).
RETURNS
void.
BOOL IsErrorSet(ERRORTYPE
eType)
EType Error
code to check. Multiple error codes within the same group can be combined to
check several errors at once. For example: if eType is ALLGROUPx then all
errors in that group are checked to see if they are set.
RETURNS
TRUE if any error specified by eType is set.
BOOL IsAnyErrorSet()
Checks all groups to see if there are any
errors set.
RETURNS TRUE
if any error is set.
void ClearAllErrors()
Clears all errors in all groups. This does
NOT clear any error text strings (see the error text section).
RETURNS void.
ERRORTYPE GetFirstError()
Gets the first error code
stored in the object (not necessarily the first error that was set).
RETURNS The first error code
among all the groups. NOERROR if no error has been set.
ERRORTYPE GetNextError()
Gets the next error in the series that began with a call to
GetFirstError(), in the order in which they are stored in the object (not the
sequence that they were set).
RETURNS: The next error in the list. NOERROR if there are no more
errors.
Since the client objects using the error object may want to set and get textual descriptions of the errors, these functions allow an array of error text information to be registered, read from, and written to. The TextErrorType array could be initialized and never changed, or the Set functions could be used to modify the text or add additional information, such as filenames that were open when the error occurred.
NOTE: These functions make no assumption about whether the error is actually set. Instead, they return only text descriptions of what the error means. For example, the GetFirstError() and GetNextError() functions could be called to get specific errors, then call the GetErrorText() functions to retrieve the detailed description of that error.
void RegisterErrorText(TextErrorType*
errors)
Registers an array of supported error text messages. This must be
called before any call to any of the GetErrorText(), SetErrorText() functions.
The structure of the TextErrorType is as follows:
#define MAXTEXTSIZE 80
struct TextErrorType {
ERRORTYPE eType;
TCHAR szText[MAXTEXTSIZE + 1];
TCHAR szTextEx[MAXTEXTSIZE + 1];
UINT uID;
};
//
Example Error Text Array
struct TextErrorType eText[] = {
// error type and
descriptions
NOERRORFLAGS, , , 0
};
The last entry in the array must have NOERRORFLAGS for the eType, which
terminates the array.
NOTE: szTextEx can only be initialized to an empty string, as it has no
string resource ID. The szTextEx is intended to be set by a function that
wishes to report an error, and is used to record information about that
specific error event.
NOTE: The uID is not used by the Generic Error Object. It is intended
to be used by the parent object to load the szText string from a string
resource, using the uID as the string ID.
NOTE: Error flags that are not included in this array will not cause an error,
but in that case the GetErrorText functions will return NULL for the error
text.
RETURNS: void.
const TCHAR* GetErrorText(ERRORTYPE
eType)
Gets a text string
describing the specified error code, using the text set by RegisterErrorText().
This string has to be internationalized. The object maintains the buffer for
the string, but by declaring it as const the compiler will disallow
modifications to the string.
eType Error code. Can
only define a single error code (i.e. do not combine with other error codes).
RETURNS Pointer to the text
string. Returns NULL if the object does not have a string for this error or if
eType = NOERROR or if eType defines
more than one error.
NOTE: When designing the implementation of this
function it may be necessary to add additional functions and/or data structures
to provide the list of error text. For example, an array of errorcodes and text
could be created and passed to the constructor or registered via a function
call. If so, it is recommended that this be a protected function and that the
text be created and passed in a subclass. This subclass would be a specific
error object, rather than a generic error object. The generic class would
provide this function to perform the search, while the subclass would provide
the text itself.
const TCHAR* GetErrorTextEx(ERRORTYPE
eType)
Gets a text string
describing more details about the specified error code, using the text set by
SetErrorTextEx(). This string has to be internationalized. The intent is for
the module producing the error to add any additional data about this specific
error, such as a filename.
eType Error code. Can
only define a single error code (i.e. do not combine with other error codes).
RETURNS Pointer to the text
string. Returns NULL if the object does not have a string for this error or if
eType = NOERROR or if eType defines
more than one error.
void SetErrorText(ERRORTYPE
eType, const TCHAR* szText)
Sets a text string
describing the specified error code, using the eType set by
RegisterErrorText(). Copies no more than MAXTEXTSIZE characters. This sets the
text that will be returned by GetErrorText() for this error. This string is
initialized by the RegisterErrorText() function, and can be modified by this
funtion.
eType Error code. Can
only define a single error code (i.e. do not combine with other error codes).
RETURNS void.
NOTE: If more than one flag is set in eType, only
the first one will have the error text set.
void SetErrorTextEx(ERRORTYPE
eType, const TCHAR* szText)
Sets a text string
describing additional data about the specified error code, using the eType set
by RegisterErrorText(). Copies no more than MAXTEXTSIZE characters. This sets
the text that will be returned by GetErrorTextEx() for this error. This string
is initialized by the RegisterErrorText() function.
eType Error code. Can
only define a single error code (i.e. do not combine with other error codes).
RETURNS void.
NOTE: If more than one flag is set in eType, only
the first one will have the error text set.
Example PDL for setting an error is:
#define ErrorCode1 (ERRORFLAG1
| GROUP1)
CErrorObject err
code
IF (error condition exists) THEN
err.SetError(ErrorCode1)
END IF
The PDL for displaying text for each error that occurred is:
CErrorObject err
ERRORTYPE errCode
code
ErrCode = err.GetFirstError()
WHILE (ErrCode != NOERROR) THEN
Write
err.GetErrorText(ErrCode)
ErrCode =
err.GetNextError()
END WHILE
The PDL for checking several errors at once within
the same group is:
CErrorObject
err
BOOL bIsErr
code
bIsErr = err.IsErrorSet(ERRCODE1 | ERRCODE2 | ERRCODE3)
// bIsErr will be TRUE if one or more of the specified errors are set
There is no reason why there could not be multiple error objects. It may be useful to create an aggregate object that holds multiple error objects, each object responsible for certain classes of errors.
If an hDestWin is passed to the constructor, then each time SetError() is called, it will send a wMsg message to the application. The intent is to allow the application to queue errors for later display, rather than display them as they occur. The application's main message loop accepted the message and, if it had not displayed the errors yet, posted a message to itself. In the application, this allowed a single user action to have a single error message, rather than a series of error messages.
The pseudo-code is:
User did something that caused SetError() to be called
SendMessage(wMsg)
Receive wMsg from SetError()
IF (errors have not been displayed yet) THEN
PostMessage(WM_DISPLAYALLERRORS)
ENDIFReceive Message WM_DISPLAYALLERRORS
Use GetFirstError() and GetNextError() to make a single error message string.
Display the error message.
SendMessage() causes the SetError() message to be immediately handled. PostMessage() causes the error display to be delayed until the application is back to checking messages in its message queue.
The sequence diagram is (in this case, wMsg was defined as the constant WM_CHECKERROROBJECT):
