|
Boost : |
From: scleary_at_[hidden]
Date: 2002-06-14 13:54:26
> From: Ted Byers [mailto:r.ted.byers_at_[hidden]]
> > But now consider: on the Win32 platform, in the case of an access
violation
> > (the most common "fatal" programming error), an OS exception (not a C++
> > exception) is raised. Traceable exceptions can only trace the stack
down to
> > the point where the OS exception is translated to a C++ exception, not
at
> > the actual point of error. If you're a Borland user, the first
catch()for
> > a VCL object will make this translation; if you don't use the VCL, this
> > translation is never made. I'm not sure how MSVC handles this.
>
> But this is easy to accomodate. First, check back to Schobi's site in a
> little while, as I just sent him an updated version of my prototype. In
it,
> you will see I have added specializations of my template class for both
the
> standard C++ exceptions and VCL exception. Then, I added a number of
macros
> and utility functions (whose usage requires creation of a couple string
> variables which ideally ought to be static). I think these macros and
> utility functions greatly ease the use of this library.
When the OS exception is translated to a VCL exception, all stack
information is lost (for the common example, all you get is the helpful
message "Access Violation" -- gee, thanx, Borland! ;) ). What you describe
above would work (i.e., get the full stack) only if your macros were used in
the function that caused the OS exception and every function in-between that
and the final catch.
OTOH, you would have all stack information from the point of translation
outward, and maybe that would be good enough...
> Now, whether you're using the VCL or not, you can use structured exception
> handling, as described in the win32 programmer's reference. To use them
in
> conjunction with my traceable exceptions, there are a number of obvious
> options.
There's no question that OS exceptions can be translated to C++ exceptions.
I was just pointing out that traceable exceptions can't keep track of the
stack until after the translation is made.
So, it comes back down to intrusiveness: I am writing some library. Do I
include traceable exception macros in every function? Or maybe just every
non-"trivial" function? I want my library to have good fatal
error-reporting capabilities (i.e., a nice stack trace when it unexpectedly
fails to avoid user reports like "it says Access Violation"), but I need it
to be efficient as well.
What about exception-neutrality? If I call user code (either explicitly
through a callback, or implicitly through a template parameter), and my
function is supposed to be exception-neutral, how would I keep track of the
stack? The only way I can see to make this work is to force *everyone* to
use traceable exceptions...
> > Also, I did not want stack tracing added to other "non-fatal"
exceptions. I
> > do use exceptions to indicate errors for operations that can be retried,
and
> > in one case I even use an exception as an end-of-input designator for a
file
> > parser (whose design is patterned after Iterators in the Python
language).
> > Adding stack traces for these exceptions is just adding overhead.
>
> But I have designed my library so that all my traceable exceptions are
> rethrown and all others are converted into one of my traceable exceptions
> which is then thrown.
Right. We're saying the same thing: ALL exceptions (access violations, "try
again later" errors, and EOF indicators) have their stacks (partially)
traced when using traceable exceptions. With my current non-intrusive
solution, the stack tracing is not done unless it is an OS exception.
Which brings up what may very well be the 2nd design question: where
*should* stack tracing be applied? My own solution will *not* apply it to
C++ exceptions, even if they reach the outermost catch handler (this was by
design). Yours always applies stack tracing to any exception, but could be
changed to not translate/trace a certain type of C++ exception.
We've been focusing on tracing the stack from the point an exception was
thrown. My solution traces the call stack; yours the "try" stack -- but
they both focus on the *exception*. Maybe this is wrong; the original post
in this thread was about *assertions*. The difference is that we can
determine at the point of assertion failure the need for a stack trace; my
solution could implement Assert as an OS access violation caught
immediately, and your solution could implement Assert by throwing a
traceable exception -- and then not translating other exception types in
your CATCH macros. What do you think; would this be a more usable design?
Just thinking out loud, there... :)
> > I think the biggest issue, though, is intrusive vs. non-intrusive
design.
>
> I am not so sure. I think the issue here will depend on the skills of the
> programmer and how long it will take to do the one or the other. I'd bet
> that even with my current project, it would take me longer to figure out
how
> to implement a non-intrusive design, and then implement and prove it
> correct, than it would to add my string variables and macros to each
> function in even a medium sized project.
Intrusiveness begs the questions: you add it to your code. Good. Are you
going to add it to the Boost.SmartPtr library? Are you going to add it to
your own general-purpose library, that you plan to put on your web page?
What about that commercial XML parser library your project uses? Where does
it stop?
A non-intrusive solution sidesteps this question, almost. ;)
> For a more skilled programmer, the
> opposite may well be true.
I doubt it; even though I'm for the non-intrusive solution, I'll freely
admit it's *much* more complicated! :) And will take many Boosters a good
amount of time to get right.
But I'd rather invest the time, and have a good, well-established,
non-intrusive solution; even if it does take longer to write.
-Steve
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk