reading <http://docs.wxwidgets.org/trunk/overview_exceptions.html>, I'm confused about exception
handling in wxWidgets.
As we use exceptions in our app, I have set wxUSE_EXCEPTIONS to 1 when compiling wxWidgets. Last
week I had a case where an exception was inadvertently thrown due to a program bug in an
EVT_PAINT event handler method. If wxWidgets had never caught this exception in
wxEvtHandler::SafelyProcessEvent() and let the application crash normally, the debugger had
presented the entire stack trace up to where the exception originated. However, catching and
re-throwing the exception obviously alters the stack, so when the exception is finally thrown
again, its original origin cannot be learned from the stack trace (I eventually enabled the
catching of first chance exceptions to find that place and the bug).
Moreover, presenting the dialog box to the user in wxApp::OnExceptionInMainLoop() seems to make
matters worse, as the program is kept alive (and further handling messages) even though before a
(fatal) exception has been thrown.
Given my limited knowledge of wxWidgets internals, I'm probably missing something, but I was
wondering why the library bothers with catching those exceptions at all:
Instead, why not omit all the above mentioned exception handling in wxWidgets, and instead, as
is suggested in the Exceptions Overview, have the user override wxApp::OnRun() and wrap the call
to the base class method in a try...catch block himself?
I'm aware that we could easily achieve this by setting wxUSE_EXCEPTIONS to 0, but the
documentation seems to suggest that we need this setting generally(?) enabled when C++
exceptions are used in application code.
Best regards,
Carsten
--
Cafu - the open-source Game and Graphics Engine
for multiplayer, cross-platform, real-time 3D Action
Learn more at http://www.cafu.de
CF> As we use exceptions in our app, I have set wxUSE_EXCEPTIONS to 1 when compiling wxWidgets. Last
CF> week I had a case where an exception was inadvertently thrown due to a program bug in an
CF> EVT_PAINT event handler method. If wxWidgets had never caught this exception in
CF> wxEvtHandler::SafelyProcessEvent() and let the application crash normally,
This is rarely what you want to happen though, is it? So usually you would
still want to catch this exception somewhere and at least notify the user
why are you exiting. OnExceptionInMainLoop() provides you a place to do it.
CF> the debugger had presented the entire stack trace up to where the
CF> exception originated.
When you're debugging an unwanted/unexpected exception it should be much
simpler to just break on throw instead.
CF> Moreover, presenting the dialog box to the user in
CF> wxApp::OnExceptionInMainLoop() seems to make matters worse, as the
CF> program is kept alive (and further handling messages) even though
CF> before a (fatal) exception has been thrown.
Of course, if the exception is really fatal, it's a wrong thing to do. But
we can't know if it is or not and it seems better to allow the user to
decide.
OTOH handling of other events should probably/surely be suspended while
this message box is shown...
CF> Given my limited knowledge of wxWidgets internals, I'm probably missing
CF> something, but I was wondering why the library bothers with catching
CF> those exceptions at all:
Under GTK we absolutely must do it because letting an exception escape
from a C callback will simply crash the program. Under Windows we could
have avoided it but it seems better to be consistent and IME providing
OnExceptionInMainLoop() is convenient because it allows you to handle all
the exceptions occurring anywhere in the program in one place.
CF> I'm aware that we could easily achieve this by setting wxUSE_EXCEPTIONS
CF> to 0
You could also override OnExceptionInMainLoop() to do something different.
BTW, while I don't think there is a problem with OnExceptionInMainLoop()
per se, I am well conscious of the fact that the current state of
exceptions support in wx is ideal, ideas about how it could be improved
would be welcome.
Regards,
VZ
--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/
many thanks for your reply!
Am 2010-11-15 01:10, schrieb Vadim Zeitlin:
> On Sun, 14 Nov 2010 20:57:07 +0100 Carsten Fuchs<Carste...@T-Online.de> wrote:
>
> CF> As we use exceptions in our app, I have set wxUSE_EXCEPTIONS to 1 when compiling wxWidgets. Last
> CF> week I had a case where an exception was inadvertently thrown due to a program bug in an
> CF> EVT_PAINT event handler method. If wxWidgets had never caught this exception in
> CF> wxEvtHandler::SafelyProcessEvent() and let the application crash normally,
>
> This is rarely what you want to happen though, is it?
Well, why not?
From a users perspective, the program is crashing, and insofar an uncaught exception is not
much different from a GPF or any other fatal program error.
The logic in wxEvtHandler::SafelyProcessEvent() just wraps uncaught exceptions and the
subsequent dialog (under wxMSW) seems to suggest that there are really some alternatives when
there are not, and makes debugging harder.
Imho, when a program crashes, it should crash "properly" / "thoroughly", not obfuscate the problem.
I'm currently wondering if OnFatalException() wouldn't be a better place to deal with uncaught
exceptions?
If this also triggered for regular uncaught C++ exceptions (just a guess, please deny or
confirm), wouldn't this be a much better place to handle the fact? If I understand correctly, we
would be able to produce the original stack trace there, explain the issue to the user, and do
whatever other custom handling is desired.
It would also not suggest that uncaught exceptions are normal program behaviour.
> When you're debugging an unwanted/unexpected exception it should be much
> simpler to just break on throw instead.
Well, yes, this is what I eventually did, but it requires possibly cumbersome skipping of
ordinary exceptions and cannot easily be done on the users system...
> CF> Moreover, presenting the dialog box to the user in
> CF> wxApp::OnExceptionInMainLoop() seems to make matters worse, as the
> CF> program is kept alive (and further handling messages) even though
> CF> before a (fatal) exception has been thrown.
>
> Of course, if the exception is really fatal, it's a wrong thing to do. But
> we can't know if it is or not and it seems better to allow the user to
> decide.
What decision does the user really have?
- Quit the program (seemingly "normally" but instantly nevertheless)
- Quit the program (from re-throwing the exception)
- Ignore the exception and try to continue (is this ever a good idea? we got where we are
because the exception was uncaught, a problem of -imho- similar severity as NULL pointer access)
> Under GTK we absolutely must do it because letting an exception escape
> from a C callback will simply crash the program.
Oh. Doesn't it produce a stack trace like under Windows then?
In the same spirit as above, would it trigger a signal so that we got into OnFatalException()?
> Under Windows we could
> have avoided it but it seems better to be consistent and IME providing
> OnExceptionInMainLoop() is convenient because it allows you to handle all
> the exceptions occurring anywhere in the program in one place.
> CF> I'm aware that we could easily achieve this by setting wxUSE_EXCEPTIONS
> CF> to 0
>
> You could also override OnExceptionInMainLoop() to do something different.
Well, yes, but in OnExceptionInMainLoop() it's too late anyway (at least in my programs, I'd not
want to continue in such cases; rather have an alternative while it still works, such as an
auto-save timer etc.), the original stack trace is lost, and there is still the alternative to
wrap wxApp::OnRun in try...catch.
> BTW, while I don't think there is a problem with OnExceptionInMainLoop()
> per se, I am well conscious of the fact that the current state of
> exceptions support in wx is ideal, ideas about how it could be improved
> would be welcome.
(you probably meant "not ideal"?)
Any suggestion I might come up with is certainly in danger of breaking backwards compatibility
and probably won't cover every use-case, but assuming that OnFatalException() also triggers for
uncaught C++ exceptions under all (most?) platforms as outlined above (OnFatalException() seems
also like a good place to me to inform the user, save minidumps, etc.), I personally would
prefer if wxWidgets did not catch exceptions that it cannot authoritatively / fully handle (of
course it should handle exceptions that are in its responsibility, if any).
CF> > CF> As we use exceptions in our app, I have set wxUSE_EXCEPTIONS to 1 when compiling wxWidgets. Last
CF> > CF> week I had a case where an exception was inadvertently thrown due to a program bug in an
CF> > CF> EVT_PAINT event handler method. If wxWidgets had never caught this exception in
CF> > CF> wxEvtHandler::SafelyProcessEvent() and let the application crash normally,
CF> >
CF> > This is rarely what you want to happen though, is it?
CF>
CF> Well, why not?
Let me rephrase this: some people may want to do it but not everybody
does. Maybe "rarely" was wrong but I'm pretty sure that everybody won't
agree with simply crashing because of an uncaught exception. So instead of
trying to solve the old ideological debate between the two schools of
thought, one claiming that an uncaught exception is always fatal while the
other one claiming that it can, sometimes, be recovered from, we just allow
the library user to decide how does he want his application to behave.
CF> From a users perspective, the program is crashing, and insofar an
CF> uncaught exception is not much different from a GPF or any other fatal
CF> program error.
I happen to disagree with this ...
CF> The logic in wxEvtHandler::SafelyProcessEvent() just wraps uncaught
CF> exceptions and the subsequent dialog (under wxMSW) seems to suggest
CF> that there are really some alternatives when there are not,
... and that. But, again, it's ok to disagree as long as everybody can
implement his own preferred strategy.
Basically, OnExceptionInMainLoop() is the replacement for a try/catch
block that you can put around the entire body of your main function in
console programs. You may not want to use it but it's very useful for those
who do.
CF> I'm currently wondering if OnFatalException() wouldn't be a better
CF> place to deal with uncaught exceptions?
I think it may be important to distinguish between crashes and unhandled
C++ exceptions. So while we might want to call some other function
(DoHandleCrashOrUnhandledException()?) from both OnFatalException() and
OnExceptionInMainLoop(), I don't think we should call OnFatalException()
itself for C++ exceptions.
CF> If this also triggered for regular uncaught C++ exceptions (just a
CF> guess, please deny or confirm),
It's not triggered for them.
CF> What decision does the user really have?
CF> - Quit the program (seemingly "normally" but instantly nevertheless)
CF> - Quit the program (from re-throwing the exception)
CF> - Ignore the exception and try to continue (is this ever a good
CF> idea? we got where we are because the exception was uncaught, a problem
CF> of -imho- similar severity as NULL pointer access)
If your code is exception-safe (and it is, right?), an exception in some
event handler doesn't need to be fatal. I.e. you might still be able to
save your work and exit the program gracefully even after getting it.
CF> > Under GTK we absolutely must do it because letting an exception escape
CF> > from a C callback will simply crash the program.
CF>
CF> Oh. Doesn't it produce a stack trace like under Windows then?
I'm not sure. You can easily test it by inserting a throw statement in one
of GTK callbacks in e.g. src/gtk/window.cpp and seeing what happens.
CF> In the same spirit as above, would it trigger a signal so that we got
CF> into OnFatalException()?
Probably but I didn't test this.
CF> > BTW, while I don't think there is a problem with OnExceptionInMainLoop()
CF> > per se, I am well conscious of the fact that the current state of
CF> > exceptions support in wx is ideal, ideas about how it could be improved
CF> > would be welcome.
CF>
CF> (you probably meant "not ideal"?)
Yes, of course, sorry for the typo.
CF> Any suggestion I might come up with is certainly in danger of breaking
CF> backwards compatibility and probably won't cover every use-case, but
CF> assuming that OnFatalException() also triggers for uncaught C++
CF> exceptions under all (most?) platforms as outlined above
CF> (OnFatalException() seems also like a good place to me to inform the
CF> user, save minidumps, etc.), I personally would prefer if wxWidgets did
CF> not catch exceptions that it cannot authoritatively / fully handle (of
CF> course it should handle exceptions that are in its responsibility, if
CF> any).
Again, for me it seems useful to have a "safety net" equivalent to
try/catch around the entire body of main(). OnExceptionInMainLoop()
provides you with a place to handle all exceptions (of the given type)
globally anywhere. One possible use of it is to replace the default
wxTheAssertHandler with the one that throws an exception if assert failed
and catch all "debug" exceptions there -- without necessarily aborting the
program. There are other examples, e.g. in an XRC-based program failure to
load some XRC resource (or to find something we expect to have in it) is
serious and unexpected enough to warrant an exception but not fatal enough
to mandate the program termination.
many thanks for your clarifications and help!
I agree that this is something where people can wonderfully disagree. ;-)
That is, I see that some wxWidgets users prefer the "safe" approach to have a chance to handle
otherwise uncaught exceptions, and others who want them uncaught, e.g. in order to
(automatically with wxDebugReport or manually) obtain a stack trace that points to the code that
originally threw the exception.
I'd be very happy if everyone could use his preferred strategy, but unfortunately, with
wxWidgets as-is (and wxUSE_EXCEPTIONS at 1), we can't!
As you asked for ideas about how exception handling could be improved, let me elaborate:
Am 2010-11-16 22:31, schrieb Vadim Zeitlin:
> [...], we just allow
> the library user to decide how does he want his application to behave.
> [...] it's ok to disagree as long as everybody can
> implement his own preferred strategy.
>
> Basically, OnExceptionInMainLoop() is the replacement for a try/catch
> block that you can put around the entire body of your main function in
> console programs. You may not want to use it but it's very useful for those
> who do.
The problem is that it exists at all, in the sense that the exceptions that it catches are
re-thrown, with a call stack that is different from the stack that went with the original exception.
It would of course be possible to eliminate the catch-all from wxWidgets by setting
wxUSE_EXCEPTIONS to 0, but I don't think that that is the proper solution. As I understand it,
wxUSE_EXCEPTIONS generally means that wxWidgets should be exception-aware (and possibly throw
its own exceptions someday in the future).
So setting wxUSE_EXCEPTIONS to 0 to turn off just the "catch all uncaught exceptions" feature
doesn't seem the right solution.
So the only way to turn this off is currently editing the wxWidgets source code; that is, in
some cases it's impossible for the library user to do.
Therefore, my proposal is the opposite:
Remove the catch-all from wxWidgets (even or especially when wxUSE_EXCEPTIONS is 1).
Instead, document the way to turn it on, i.e. the wxWidgets-equivalent to main() wrapped in
try...catch. I think this is even in the docs already:
http://docs.wxwidgets.org/trunk/overview_exceptions.html about wxApp::OnRun(), and similarly at
wxAppConsole::HandleEvent().
This way it would even be possible to remove method wxAppConsole::OnUnhandledException(), and
OnExceptionInMainLoop() (because there is HandleEvent() already).
The resulting behaviour would be simpler, more flexible (can catch-all turn on/off as explained
above), and to new users unsurprising and as expected. And the code would be shorter. :-)
CF> > Basically, OnExceptionInMainLoop() is the replacement for a try/catch
CF> > block that you can put around the entire body of your main function in
CF> > console programs. You may not want to use it but it's very useful for those
CF> > who do.
CF>
CF> The problem is that it exists at all, in the sense that the exceptions
CF> that it catches are re-thrown, with a call stack that is different from
CF> the stack that went with the original exception.
So in fact the problem is [only] that you can't use post-mortem debugging,
right? This doesn't seem very serious to me because you can still see the
throw location under debugger but I agree that it would be better to
[be able to] preserve this information.
The only way to do it that I see is to refactor and virtualize the
contents of wxEventLoopManual::Run() and wxEvtHandler::SafelyProcessEvent()
e.g. by forwarding them to some (new) wxAppTraits method(s). This would
allow to keep the current code by default but allow you to override these
methods to avoid having a try/catch block in them. This is not ideal
because it's a bit complex and also introduces extra overhead to each event
processing but I don't see anything better right now.
CF> Instead, document the way to turn it on, i.e. the wxWidgets-equivalent
CF> to main() wrapped in try...catch. I think this is even in the docs
CF> already: http://docs.wxwidgets.org/trunk/overview_exceptions.html about
CF> wxApp::OnRun(), and similarly at wxAppConsole::HandleEvent(). This way
Unfortunately the documentation is wrong, putting try/catch in overridden
OnRun() will not work in wxGTK. I had written this text before learning
about the problem with propagating C++ exceptions through C (GTK+) layer
and introducing OnExceptionInMainLoop() to work around this.
That, and as well that
- this feature is implicitly "on" by default and cannot be turned "off",
- it is somewhat unexpected (as implicit),
- it seems to duplicate the standard libraries terminate() mechanism.
All this is of course not very serious indeed. I brought it up as I spent a lot of time
(remote-)debugging a problem on a users machine (that was complicated both by the fact that things
were remote and the (for me, for a while) unexpected "catch all" mechanism in wxWidgets).
(Another idea (quite the opposite from above) for wxWidgets code might be to catch "const
std::exception& e" before catching "...", and if we caught e, inspect it (e.g. typeid(e), e.what())
to provide additional information to the user. If we caught "..." instead, process as before.)
CF> On 18.11.2010 13:54, Vadim Zeitlin wrote:
CF> > So in fact the problem is [only] that you can't use post-mortem debugging,
CF> > right?
CF>
CF> That, and as well that
CF> - this feature is implicitly "on" by default and cannot be turned "off",
I'd welcome any patches virtualizing the relevant code to allow turning it
off but unfortunately I don't have any possibility to work on this myself
at the moment.
CF> - it is somewhat unexpected (as implicit),
It definitely should be documented better in the exception handling
overview.
CF> - it seems to duplicate the standard libraries terminate() mechanism.
I think that the standard library terminate() mechanism is perfectly
useless in practice. I.e. you just never want terminate() to be called IMO.
CF> (Another idea (quite the opposite from above) for wxWidgets code might
CF> be to catch "const std::exception& e" before catching "...", and if we
CF> caught e, inspect it (e.g. typeid(e), e.what()) to provide additional
CF> information to the user. If we caught "..." instead, process as
CF> before.)
We could add this to the default OnUnhandledException() implementation
inside "#if wxUSE_STD_DEFAULT" or something like this but then to be honest
I expect any wx program really using exceptions to override
OnUnhandledException() anyhow. This should probably be made more clear in
the documentation however.