this macro only does anything if wxDEBUG_LEVEL >= 2.
*/
#if wxDEBUG_LEVEL
- // call this function to break into the debugger unconditionally (assuming
- // the program is running under debugger, of course)
- extern void WXDLLIMPEXP_BASE wxTrap();
-
- // assert checks if the condition is true and calls the assert handler with
- // the provided message if it isn't
+ // wxTrap() can be used to break into the debugger unconditionally
+ // (assuming the program is running under debugger, of course).
+ //
+ // If possible, we prefer to define it as a macro rather than as a function
+ // to open the debugger at the position where we trapped and not inside the
+ // trap function itself which is not very useful.
+ #ifdef __VISUALC__
+ #define wxTrap() __debugbreak()
+ #else
+ extern WXDLLIMPEXP_BASE void wxTrap();
+ #endif // Win VisualC
+
+ // Global flag used to indicate that assert macros should call wxTrap(): it
+ // is set by the default assert handler if the user answers yes to the
+ // question of whether to trap.
+ extern WXDLLIMPEXP_DATA_BASE(bool) wxTrapInAssert;
+
+ // This macro checks if the condition is true and calls the assert handler
+ // with the provided message if it isn't and finally traps if the special
+ // flag indicating that it should do it was set by the handler.
//
- // NB: the macro is defined like this to ensure that nested if/else
- // statements containing it are compiled in the same way whether it is
- // defined as empty or not; also notice that we can't use ";" instead
- // of "{}" as some compilers warn about "possible unwanted ;" then
+ // Notice that we don't use the handler return value for compatibility
+ // reasons (if we changed its return type, we'd need to change wxApp::
+ // OnAssertFailure() too which would break user code overriding it), hence
+ // the need for the ugly global flag.
#define wxASSERT_MSG(cond, msg) \
- if ( !wxTheAssertHandler || (cond) ) \
- {} \
- else \
- wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, #cond, msg)
+ wxSTATEMENT_MACRO_BEGIN \
+ if ( wxTheAssertHandler && !(cond) && \
+ (wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, \
+ #cond, msg), wxTrapInAssert) ) \
+ { \
+ wxTrapInAssert = false; \
+ wxTrap(); \
+ } \
+ wxSTATEMENT_MACRO_END
// a version without any additional message, don't use unless condition
// itself is fully self-explanatory
// wxFAIL is a special form of assert: it always triggers (and so is
// usually used in normally unreachable code)
- #define wxFAIL_COND_MSG(cond, msg) \
- if ( !wxTheAssertHandler ) \
- {} \
- else \
- wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, cond, msg)
+ #define wxFAIL_COND_MSG(cond, msg) \
+ wxSTATEMENT_MACRO_BEGIN \
+ if ( wxTheAssertHandler && \
+ (wxOnAssert(__FILE__, __LINE__, __WXFUNCTION__, \
+ #cond, msg), wxTrapInAssert) ) \
+ { \
+ wxTrapInAssert = false; \
+ wxTrap(); \
+ } \
+ wxSTATEMENT_MACRO_END
+
#define wxFAIL_MSG(msg) wxFAIL_COND_MSG("Assert failure", msg)
#define wxFAIL wxFAIL_MSG((const char*)NULL)
#else // !wxDEBUG_LEVEL
void OnThrowUnhandled(wxCommandEvent& event);
void OnCrash(wxCommandEvent& event);
+ void OnTrap(wxCommandEvent& event);
#if wxUSE_ON_FATAL_EXCEPTION
void OnHandleCrash(wxCommandEvent& event);
#endif
Except_ThrowObject,
Except_ThrowUnhandled,
Except_Crash,
+ Except_Trap,
#if wxUSE_ON_FATAL_EXCEPTION
Except_HandleCrash,
#endif // wxUSE_ON_FATAL_EXCEPTION
EVT_MENU(Except_ThrowObject, MyFrame::OnThrowObject)
EVT_MENU(Except_ThrowUnhandled, MyFrame::OnThrowUnhandled)
EVT_MENU(Except_Crash, MyFrame::OnCrash)
+ EVT_MENU(Except_Trap, MyFrame::OnTrap)
#if wxUSE_ON_FATAL_EXCEPTION
EVT_MENU(Except_HandleCrash, MyFrame::OnHandleCrash)
#endif // wxUSE_ON_FATAL_EXCEPTION
menuFile->Append(Except_ThrowUnhandled,
wxT("Throw &unhandled exception\tCtrl-U"));
menuFile->Append(Except_Crash, wxT("&Crash\tCtrl-C"));
+ menuFile->Append(Except_Trap, "&Trap\tCtrl-T",
+ "Break into the debugger (if one is running)");
menuFile->AppendSeparator();
#if wxUSE_ON_FATAL_EXCEPTION
menuFile->AppendCheckItem(Except_HandleCrash, wxT("&Handle crashes\tCtrl-H"));
DoCrash();
}
+void MyFrame::OnTrap(wxCommandEvent& WXUNUSED(event))
+{
+ wxTrap();
+}
+
#if wxUSE_ON_FATAL_EXCEPTION
void MyFrame::OnHandleCrash(wxCommandEvent& event)
#if wxDEBUG_LEVEL
// break into the debugger
+#ifndef wxTrap
+
void wxTrap()
{
#if defined(__WINDOWS__) && !defined(__WXMICROWIN__)
#endif // Win/Unix
}
+#endif // wxTrap already defined as a macro
+
// default assert handler
static void
wxDefaultAssertHandler(const wxString& file,
#if wxDEBUG_LEVEL
+bool wxTrapInAssert = false;
+
static
bool DoShowAssertDialog(const wxString& msg)
{
MB_YESNOCANCEL | MB_ICONSTOP ) )
{
case IDYES:
- wxTrap();
+ // If we called wxTrap() directly from here, the programmer would
+ // see this function and a few more calls between his own code and
+ // it in the stack trace which would be perfectly useless and often
+ // confusing. So instead just set the flag here and let the macros
+ // defined in wx/debug.h call wxTrap() themselves, this ensures
+ // that the debugger will show the line in the user code containing
+ // the failing assert.
+ wxTrapInAssert = true;
break;
case IDCANCEL: