/* * _DEBUG.H * * Header file for ArchiveLib 2.0 * * Copyright (c) 1994-1996 Greenleaf Software, Inc. * All Rights Reserved * * DESCRIPTION * * These macros and types are all used in the debug versions of the * ArchiveLib. * * MACROS * * _ALAssertFailure() * AL_ASSERT() * AL_ASSERT_OBJECT() * AL_CLASS_TAG() * * PROTOTYPES: * * IsBadWritePtr() * * ENUMERATED TYPES: * * _ALClassTags * * REVISION HISTORY * * May 26, 1994 1.0A : First release * * August 10, 1994 1.0B : A few changes to help out UNIX. * * February 14, 1996 2.0A : New release */ #ifndef __DEBUG_H #define __DEBUG_H /* * _ALAssertFailure is the function called by AL_ASSERT() and * AL_ASSERT_OBJECT() when their assertion fails. */ AL_LINKAGE void AL_CFUNCTION _ALAssertFailure( const char AL_DLL_FAR *condition, const char AL_DLL_FAR *filename, int line, const char AL_DLL_FAR *message, ... ); #ifdef NDEBUG /* * In the non-debug versions, both of these macros basically go away. * The only difficulty is trying to avoid having the compilers generate * error messages when they see this code. Maybe in NDEBUG mode I could * change these to inline functions that do nothing? */ #define AL_ASSERT( condition, message ) ((void) 0) /* Tag debug macro */ #define AL_ASSERT_OBJECT( pointer, class, message ) ((void) 0) /* Tag debug macro */ #else /* * In debug mode, AL_ASSERT() tests the condition, and generates * an abort with an error message when the condition fails. */ #define AL_ASSERT( condition, message ) /* Tag debug macro */ \ ( ( condition ) ? \ (void) 0 : \ _ALAssertFailure( #condition, \ __FILE__, \ __LINE__, \ message ) ) \ /* * I can only call IsBadWritePtr() if TOOLHELP.DLL is available. As far * as I know, it won't be available with any of the DOS Extenders * The only way we have access to TOOLHELP.DLL is if we are *really* * running under Windows, not some bogus imitation. */ #if !defined( AL_WINDOWS ) # define IsBadWritePtr( p, s ) 0 #endif /* * AL_ASSERT_OBJECT() is a great macro. It is used to test the * validity of an object. This is a two step process. First, * we make sure we are dealing with a good pointer. If not, an * asserting error is triggered. This is much better than the * GPF you would normally get from a bad pointer. Next, we * test the GoodTag() macro, which verifies that this is * a properly constructed object from the specified class. Of * course, in NDEBUG mode this all goes away. * */ /* * I had to change the name of the arguments to this macro in order * to avoid a Sun CC warning message. The arguments were originally * named "pointer" and "class". I got a couple of warning messages * telling me that "macro substitutions not performed in strings", like * anyone is going to think that... */ #define AL_ASSERT_OBJECT( ptr, cls, message ) /* Tag debug macro */ \ ( ptr == 0 || IsBadWritePtr( ptr, sizeof( cls ) ) ) ? \ _ALAssertFailure( "IsBadWritePtr()", \ __FILE__, \ __LINE__, \ "%s: Bad pointer to object of class %s", \ message, \ #cls ) \ : \ ( ( (cls *)ptr)->GoodTag() ? \ (void) 0 \ : \ _ALAssertFailure( #ptr "->GoodTag()", \ __FILE__, \ __LINE__, \ "%s: %s is not an object of class %s", \ message, \ #ptr, \ #cls ) ) #endif /* * The AL_CLASS_TAG() macro assigns a new debug class and data * member to each of the classes in ArchiveLib. Each of these * debug classes uses a special integer tag (stored in the data member) * to uniquely identify itself. These are the integer values of * these integers. */ enum _ALClassTags { /* Tag debug type */ _ALDeletedObjectTag = 0, _ALStorageTag, _ALFileTag, _ALMemoryBaseTag, _ALMemoryTag, _ALHugeMemoryTag, _ALWinMemoryTag, _ALEntryTag, _ALEntryListTag, _ALArchiveTag, _ALGlArchiveTag, _ALPkArchiveTag, _ALMonitorTag, _ALBarGraphTag, _ALSpinnerTag, _ALWindowsMessageTag, _ALEngineTag, _ALCompressorTag, _ALDecompressorTag, _ALCopyCompressorTag, _ALCopyDecompressorTag, _ALGlCompressorTag, _ALGlDecompressorTag, _ALPkCompressorTag, _ALPkDecompressorTag, _ALBadEngineTag, _ALCompressedObjectTag, _ALNameTag, _ALWildCardExpanderTag, _ALOs2MessageTag, _ALToolKitTag, _ALSimpleMonitorTag }; /* * AL_CLASS_TAG( x ) is a macro that is used to help debug * ArchiveLib. The insertion of this macro in a class definition * adds a new data member and member function to the class. The * data member is an object of a class uniquely created by the * macro. The reason the data member is a class object instead * of a simple integer or character tag is this: By making it a * class object, we can automatically assign it a valid value * when constructed, and an invalid value when destroyed. * * The member function added to the class is called GoodTag(). * Once you have added AL_CLASS_TAG( x ) to your class definition, * you can call object.GoodTag() anytime you want. It will return * a true value only if the data member has the correct value, * * We make use of this function in AL_ASSERT_OBJECT(). It * checks the value of this object frequently in member functions * and destructors, generating an assertion failure if the object * doesn't look like the correct type. * * Note that the ASSERT_OBJECT() statements generate no code when the * library is compiled with NDEBUG, so this class will not be * generating much low overhead. However, the data member will * still be taking up a single byte in each instance. * * If you want to eliminate class tags, this line in will do it * You will save one byte per instance. The best way to accomplish this * is to define the macro in ALCUSTOM.H, then rebuild the library with * macro AL_CUSTOM defined in your project. After you build this new * version of the library, you must absolutely, positively, be sure * that you continue to use AL_CUSTOM and ALCUSTOM.H when working * with the library. If you don't, your library and your application * will think that most classes in ArchiveLib are different sizes, and * *nothing* will work. * *#define AL_CLASS_TAG( x ) int GoodTag(){ return 1; } */ #if defined( NDEBUG ) && !defined( AL_CLASS_TAG ) # define AL_CLASS_TAG( x ) class AL_CLASS_TYPE _ALTag##x { /* Tag debug macro */ \ public : \ unsigned char mucTagVal; \ } mMyTag; \ int AL_INLINE_PROTO GoodTag(){ return 1; } #endif /* #if defined( NDEBUG ) && !defined( AL_CLASS_TAG ) */ #if !defined( NDEBUG ) && !defined( AL_CLASS_TAG ) # define AL_CLASS_TAG( x ) class AL_CLASS_TYPE _ALTag##x { /* Tag debug macro */ \ public : \ AL_INLINE_PROTO _ALTag##x(){ mucTagVal = x; } \ AL_INLINE_PROTO ~_ALTag##x(){ mucTagVal = _ALDeletedObjectTag; } \ unsigned char mucTagVal; \ } mMyTag; \ int AL_INLINE_PROTO GoodTag(){ return mMyTag.mucTagVal == x; } #endif /* #if !defined( NDEBUG ) && !defined( AL_CLASS_TAG ) */ #endif /* #ifndef __DEBUG_H */