/** @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, http://graphics.cs.williams.edu @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 _MSC_VER // conditional expression is constant # pragma warning (disable : 4127) #endif #ifdef G3D_LINUX // Needed so we can define a global display // pointer for debugAssert. #if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */ #include #include #include #endif #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, 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, or halt the program. The assertion is also posted to the clipboard under Win32. */ /** @def alwaysAssertM(exp, msg) Same as debugAssertM except that it asserts in release builds as well. */ namespace G3D { typedef bool (*AssertionHook)( const char* _expression, const std::string& message, const char* filename, int lineNumber, 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) # define rawBreak() ::DebugBreak(); # elif defined(__i386__) // gcc on intel # define rawBreak() __asm__ __volatile__ ( "int $3" ); # else // some other gcc # define rawBreak() ::abort() # endif # 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 { \ if (!(exp)) { \ G3D::_internal::_releaseInputGrab_(); \ if ((G3D::_internal::_debugHook != NULL) && \ G3D::_internal::_debugHook((const char*)(#exp), message, __FILE__, __LINE__, __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) { \ if (!(exp)) { \ G3D::_internal::_releaseInputGrab_(); \ if ((G3D::_internal::_failureHook != NULL) && \ G3D::_internal::_failureHook(#exp, message, __FILE__, __LINE__, __debugPromptShowDialog__)) { \ ::exit(-1); \ } \ G3D::_internal::_restoreInputGrab_(); \ } \ } #endif // if debug namespace G3D { namespace _internal { #ifdef G3D_LINUX #if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */ /** 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 #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 useGuiPrompt); bool _handleErrorCheck_( const char* expression, const std::string& message, const char* filename, int lineNumber, 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