/** @file debugAssert.h debugAssert(expression); debugAssertM(expression, message); @cite John Robbins, Microsoft Systems Journal Bugslayer Column, Feb 1999. http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm @cite Douglas Cox, An assert() Replacement, Code of The Day, flipcode, Sept 19, 2000 http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1 @maintainer Morgan McGuire, matrix@graphics3d.com @created 2001-08-26 @edited 2006-01-12 Copyright 2000-2006, Morgan McGuire. All rights reserved. */ #ifndef G3D_DEBUGASSERT_H #define G3D_DEBUGASSERT_H #include #include "G3D/platform.h" #include #ifdef G3D_WIN32 // conditional expression is constant # pragma warning (disable : 4127) #endif #ifdef G3D_LINUX // Needed so we can define a global display // pointer for debugAssert. #include #include #include #endif #ifdef G3D_OSX // Need this for DebugStr() #import #endif /** @def debugBreak() Break at the current location (i.e. don't push a procedure stack frame before breaking). */ /** @def debugAssert(exp) Breaks if the expression is false. If G3D_DEBUG_NOGUI is defined, prompts at the console, otherwise pops up a dialog. The user may then break (debug), ignore, ignore always, or halt the program. The assertion is also posted to the clipboard under Win32. */ /** @def debugAssertM(exp, msg) Breaks if the expression is false and displays a message. If G3D_DEBUG_NOGUI is defined, prompts at the console, otherwise pops up a dialog. The user may then break (debug), ignore, ignore always, or halt the program. The assertion is also posted to the clipboard under Win32. */ namespace G3D { typedef bool (*AssertionHook)( const char* _expression, const std::string& message, const char* filename, int lineNumber, bool& ignoreAlways, bool useGuiPrompt); /** Allows customization of the global function invoked when a debugAssert fails. The initial value is G3D::_internal::_handleDebugAssert_. G3D will invoke rawBreak if the hook returns true. If NULL, assertions are not handled. */ void setAssertionHook(AssertionHook hook); AssertionHook assertionHook(); /** Called by alwaysAssertM in case of failure in release mode. If returns true then the program exits with -1 (you can replace this with your own version that throws an exception or has other failure modes). */ void setFailureHook(AssertionHook hook); AssertionHook failureHook(); namespace _internal { extern AssertionHook _debugHook; extern AssertionHook _failureHook; } // internal } // G3D /** @def __debugPromptShowDialog__ @internal */ #ifdef G3D_DEBUG # if defined(_MSC_VER) # if (_MSC_VER >= 1300) // VC7 or VC8 # define rawBreak() DebugBreak(); # else // MSVC6 # define rawBreak() _asm { int 3 }; # endif # elif defined(__i386__) // gcc on intel # define rawBreak() __asm__ __volatile__ ( "int $3" ); # else // some other gcc # define rawBreak() abort() # endif // old mac code: //# define rawBreak() DebugStr((const unsigned char*)("\nG3D: Invoking breakpoint in debugger.")); /* XCode must be set to break on Debugger()/DebugStr() */ # define debugBreak() G3D::_internal::_releaseInputGrab_(); rawBreak(); G3D::_internal::_restoreInputGrab_(); # define debugAssert(exp) debugAssertM(exp, "Debug assertion failure") #ifdef G3D_DEBUG_NOGUI #define __debugPromptShowDialog__ false #else #define __debugPromptShowDialog__ true #endif #define debugAssertM(exp, message) do { \ static bool __debugAssertIgnoreAlways__ = false; \ if (!__debugAssertIgnoreAlways__ && !(exp)) { \ G3D::_internal::_releaseInputGrab_(); \ if ((G3D::_internal::_debugHook != NULL) && \ G3D::_internal::_debugHook((const char*)(#exp), message, __FILE__, __LINE__, __debugAssertIgnoreAlways__, __debugPromptShowDialog__)) { \ rawBreak(); \ } \ G3D::_internal::_restoreInputGrab_(); \ } \ } while (0) #define alwaysAssertM debugAssertM #else // Release #ifdef G3D_DEBUG_NOGUI #define __debugPromptShowDialog__ false #else #define __debugPromptShowDialog__ true #endif // In the release build, just define away assertions. #define rawBreak() do {} while (0) #define debugAssert(exp) do {} while (0) #define debugAssertM(exp, message) do {} while (0) #define debugBreak() do {} while (0) // But keep the 'always' assertions #define alwaysAssertM(exp, message) { \ static bool __alwaysAssertIgnoreAlways__ = false; \ if (!__alwaysAssertIgnoreAlways__ && !(exp)) { \ G3D::_internal::_releaseInputGrab_(); \ if ((G3D::_internal::_failureHook != NULL) && \ G3D::_internal::_failureHook(#exp, message, __FILE__, __LINE__, __alwaysAssertIgnoreAlways__, __debugPromptShowDialog__)) { \ exit(-1); \ } \ G3D::_internal::_restoreInputGrab_(); \ } \ } #endif // if debug namespace G3D { namespace _internal { #ifdef G3D_LINUX /** A pointer to the X11 display. Initially NULL. If set to a non-null value (e.g. by SDLWindow), debugAssert attempts to use this display to release the mouse/input grab when an assertion fails. */ extern Display* x11Display; /** A pointer to the X11 window. Initially NULL. If set to a non-null value (e.g. by SDLWindow), debugAssert attempts to use this window to release the mouse/input grab when an assertion fails. */ extern Window x11Window; #endif /** Pops up an assertion dialog or prints an assertion ignoreAlways - return result of pressing the ignore button. useGuiPrompt - if true, shows a dialog */ bool _handleDebugAssert_( const char* expression, const std::string& message, const char* filename, int lineNumber, bool& ignoreAlways, bool useGuiPrompt); bool _handleErrorCheck_( const char* expression, const std::string& message, const char* filename, int lineNumber, bool& ignoreAlways, bool useGuiPrompt); /** Attempts to give the user back their mouse and keyboard if they were locked to the current window. @internal*/ void _releaseInputGrab_(); /** Attempts to restore the state before _releaseInputGrab_. @internal*/ void _restoreInputGrab_(); }; }; // namespace #endif