Rob Kremer, kremer@cpsc.ucalgary.ca

KSI General Purpose Library


KSI General Purpose Library

Debugging Module

The debugging module is just a collection of macros which can be convieniently used for debugging purposes. Use these macros to check for incorrect pointers, use of an object after it's destruction, to check pre- and post-conditions in your code, to declare assertions and invarients, and to write a trace file of the calls your code makes. These macros do all there output through the
error() function. These macros can be extremely useful in situtations where the usual debugging factilites can't be used for whatever reason.

The SET_TRACE and SET_DEBUG macros control the settings of the active macros (trace and condition checking macros expand to nothing at all if these macros are not set):

MTRACE(ID,ID), MTRACEOUT(ID,ID) is used at the start (end) of a member function: MTRACE(class,member_function)

MDEBUG(EXP) is used anyware, typlically: MDEBUG(error(-1,'D',"result is '%x',res))
the NDEBUG macro controls the expansion of other debugging facilities

The outline of a class using these facilities is as follows:

	class C [: public P] {
	  MDEBUG_DECL[_VIRTUAL](C); //declares a sentinal value
	  [ C() | MCONSTRUCTOR_IMP[_VIRTUAL](C) ]; 
	  [ ~C() | MDESTRUCTOR_IMP[_VIRTUAL](C) ];
	  void x();
	  }
	void C::classInvarient(char* file, int line) const { //you must include this code
	  MCLASSINVTEST(); ...
	  [P::classInvarient(file,line);] }
	C::C() {
	  MTRACE(C,C); //can't check class invarient because object isn't valid yet
	  MPRECONDITION(); ...
	  MCONSTRUCTOR_IMP_CODE[_VIRTUAL](C) //initializes the sentinal value
	  
	  POSTCONDITION(); ...
	  MEXIT_CODE(C,C); //check sentinal, call the invarient
	  }
	C::~C() {
	  MTRACE(C,~C);
	  MPRECONDITION(); ...
	  
	  MDESTRUCTOR_IMP_CODE[_VIRTUAL](C)
	  POSTCONDITION(); ...
	  MTRACEOUT(C,~C); //can't check class invarient because object isn't valid anymore
	  }
	void C::x() {
	  MENTRY_CODE(C,x); //check sentinal, call the invarient
	  MPRECONDITION(); ...
	  
	  POSTCONDITION(); ...
	  MEXIT_CODE(C,x); }  //check sentinal, call the invarient

Object Counting Module (for debugging)

class ObjectCounter (concrete)

Inherit from this class to check that objects of the child class are all being destructed properly by your program. Objects that inherit from ObjectCounter increment a static counter in ObjectCounter whenever they are constructed and decrement the counter whenever they are destructed. In addition, by making use of run-time typing, ObjectCounter keeps separate counts for each child class.

You may get a get a report on the counts of each object type by calling ObjectCounter::report(); the report will be output via the error() function in the error module.

Error Module

error() function

The error() function provides an simple interface for custom handling messages for your application. The arguments of the error() function follow the same conventions as those of the printf() family in the standard C library. The format is

void error([platform specific] handle, char severity, const char* format, ...);

The type of the handle argument is platform specific, for example it it a HWND under MS Windows and an int under DOS. The severity argument controls how error() handles the message and also acts as in indicator to the user about the kind of message it is. The remaider of the arguments are identical to those of printf() and constitute the actual message.

error() behaves as follows:

  1. if severity is not 'z' or 'Z' (interpret as "don't log" -- for messages only intented for the run-time user or to communicate with your MessageHandler [eg: blank out the status line]), the message is appended to a log file together with a timestamp. The name of the file will be same as that of the program file, but with a ".log" extention; if error() can't find the file name, it will use "error.log". Under DOS and MS Windows, this file will be placed in the C:\ directory.
  2. if (int)handle returns -1, then the message is output to stdout under command line systems or posted through a message window under windowing systems.
  3. otherwise, if severity is 'F', the message is posted as above and the user is queried to abort the program; if the user chooses the abort, the program is terminated through a call to exit(1).
  4. otherwise, if this is a windowing system and the calling application object or its main window is a subclass of ErrorHandler:
    • the interpreted text of the window and the severty argument is sent to the calling object.
    • if the message is accepted by the calling the object (message returns true), error() terminates.
    • otherwise, go on to the next step.
  5. otherwise the message is posted via stdout or a message window.
A severity of 'T' or 't' is used for formating of tracing to the log file. A 'T' is a trace-in and indentation to be incremented by single character space on subsequent traces. A 't' is a trace-out and decrements the indentation.

class ErrorHandler (abstract)

This class is used be error for custom handling of messages. It only has one public method:

virtual int postError(char severity, char* errorMessage) = 0;

Your overriding class should return 0 if it handles the message and non-zero if it doesn't handle the message (in which case it will be posted by error().

class ErrorMsgException

A base class for exception classes that contain internal messages and use error() to send these to the application.

DECL_EXCEPT_CLASS and DECL_EXCEPT_ABS_CLASS Macros

These macros can be used to easily declare and impliment exception which are subclasses of ErrorMsgException.

References Module

class Ref2 (concrete)

The Ref2 class is used to support polymorphism in STL container. Since STL is designed around direct containment, one cannot, for example, put various polymorphic Shape classes (such as Rectangle or Ellipse and expect them to maintain their exact type. One could instantiate an STL container with a pointer type, and this often is appropriate, but destruction of the referenced objects is often problematic: one can define destroy() as whether or not to destroy the referenced objects, but only one way or the other for all pointers of that type.

Ref2 behaves a lot like a pointer, but allows the programmer to control whether the referenced object is destructed for each instance of the Ref2 "pointer".

Version Module

class Version (concrete)

The Version class is used as a common way capture versions of programs. It is useful to write out the version of a program when one is storing data to disk, or transmitting data to another program. The format is simply a 32 bit wordk, but Version has methods for setting and extracting the major version number, the minor version number, the patch level, and the operating system of the program. Comparison operators == , < , > , < = , and >= are defined (but don't take the OS into account). The string cast operator is also defined which returns a formatted string in the format "M.m[.patch][ (platform)]".

Lexer Module

The Lexer class isn't really a legitimate class: its only a name space for a set of static methods used for writing and reading common types. This class will probably be redesigned to the strategy pattern [Pree 95]. Besides encapsulating these methods, this class is useful for easily enabling your program to change details of storage or transmission: you might change the storage format between pure-text and binary just by sustituting different versions of the Lexer class.

Observer Module

(
.h, .cpp)

The observer module is modeled on the observer pattern in [Gamma 95].

class Subject (concrete)

class Observer (abstract)

Command Module

(
.h, .cpp)

The command module is modeled on the command pattern in [Pree 95]. The command pattern is rather over-simplified though, so the implementation found here is considerably more complex.

class Command (concrete)

template Command1

template CommandPtr1

template Command2

class CompositeCommand (concrete)

class CommandReceiverContainer (abstract)

class CommandReceiver (abstract)

class ObjectLocator (abstract)

class NullObjectLocator (concrete)

class CommandHandler (concrete)

Platform-Independent Graphics Module

This module is still evolving! The intent of the platform-independant graphics module is to provide a common interface to graphics (not window control) primitives. This allows code to be written independent of the particular OS. The following table describes the required definitions for each OS:

TYPE IDENTIFIER DESCRIPTION
DEVICE Device context type used as first argument to most drawing functions. In MS Windows, HDC.
WINDOWHANDLE Handle for a window. In MS Windows, HWND.
MENU Handle for a menu. In MS Windows, HMENU.
CURSOR Handle for a cursor. In MS Windows, HCURSOR.
COORINATE_INDICI The type of a coordinate element used in POINT and RECTANGLE. Usually long or short.
POINT Point structure. Must contain public data members x and y.
RECTANGLE Rectangle structure. Must contain public data members left, top, right, and bottom.
COLOR Color type. In MS Windows, long.

CONSTANT IDENTIFIER DESCRIPTION
RECTANGLE_EMPTY An empty rectange. In MS Windows, {0,0,0,0}
MAXRECTANGLE The maximum possible RECTANGLE. In MS Windows, {-MAXLONG,-MAXLONG,MAXLONG,MAXLONG}.
COLOR_WHITE White. In MS Windows, 0xFFFFFFL.
COLOR_BLACK Black. In MS Windows, 0L.

FUNCTION IDENTIFIER DESCRIPTION
int MRectangle(DEVICE, RECTANGLE, COLOR* fill=NULL, COLOR* border=NULL, int width=1, unsigned long style=PS_SOLID); Draw a rectangle. Returns 0 for success.
MEllipse(DEVICE, RECTANGLE, COLOR* fill=NULL, COLOR* border=NULL, int width=1, unsigned long style=PS_SOLID); Draw an ellipse. Returns 0 for success.
int MRoundRect(DEVICE, RECTANGLE, COORDINATE_INDICI w, COORDINATE_INDICI h, COLOR* fill=NULL, COLOR* border=NULL, int width=1, unsigned long style=PS_SOLID); Draw a rounded rectangle. Returns 0 for success.
int MPolygon(DEVICE, POINT* pVector, int count, COLOR* fill=NULL, COLOR* border=NULL, int width=1, unsigned long style=PS_SOLID); Draw a closed polygon. Returns 0 for success.
int MDrawLine(DEVICE,POINT,POINT, COLOR* color=NULL, int width=1, unsigned long style=PS_SOLID); Draw a line. Returns 0 for success.
bool MIsPointInRectangle(POINT, RECTANGLE); Compute whether POINT is in RECTANGLE
bool MIsPointInEllipse(POINT, RECTANGLE); Compute whether POINT is in in an ellipse defined by RECTANGLE
bool MIsPointInRoundRect(POINT, RECTANGLE, COORDINATE_INDICI w, COORDINATE_INDICI h); Compute whether POINT is in the given rounded rectangle
bool MIsPointInPolygon(POINT, POINT* pVector, int count); Compute whether POINT is in the given polygon
MENU createPopupMenu(void) Returns a blank popup menu
bool appendMenu_String(MENU, int id, char*) Adds a string item to a the bottom of menu
bool appendMenu_Popup(MENU, MENU subMenu, char*) Adds a submenu item to a the bottom of menu
bool appendMenu_Separator(MENU) Adds a separator item to a the bottom of menu
bool destroyMenu(MENU) Destroys a menu
int trackPopupMenu(MENU& menu, POINT& pt, char* menuName, int len); displays a menu for selection at pt, returns the identifier of any selected item (or 0 if none selected). menuName if filled with text of the selected item.
RECTANGLE trackRectangle(RECTANGLE r, COMPASEPOINTS p=SE, WINDOWHANDLE hwnd=0); displays a rectangle and allows the user to resize it; returns the new, resized rectangle.
POINT trackElasticLine(POINT anchor, POINT movePoint, WINDOWHANDLE hwnd=0); Allows the user to reset one endpoint of the line defined by anchor and movePoint.

The following structures are defined for every OS:

IDENTIFIER DESCRIPTION
typedef enum {N=0x1, E=0x2, S=0x4, W=0x8, NE=0x3, NW=0x9, SW=0xC, SE=0x6} COMPASEPOINTS; Edge points of a rectangle, ellipse, rounded rectangle, etc.


Rob Kremer, kremer@cpsc.ucalgary.ca