[Please do not mail me a copy of your followup]
n...@notvalid.com spake the secret code
<s8JBx.257668$pi.9...@fx47.am4> thusly:
>If a code is done well, should it have error checking in all places?
It should have error *detection* at all the places where errors can be
detected. Usually this is from C style APIs that indicate errors by
return value because C doesn't have exceptions.
Let's take a concrete example. In COM programming, methods on COM
interfaces and COM global functions return HRESULT status codes
indicating success or failure. In some cases, an HRESULT may be
tested explicitly because it is "normal" for it to fail -- for
instance opening a file whose name is supplied on the command-line
arguments or through other user input. The user may have simply typed
the filename incorrectly and we want to handle the "can't open file"
error explicitly. In this case, failure is an expected part of normal
interaction.
In other cases, the failure is due to a programming error and we
aren't going to build user interface or other specific handling for
this error, but we don't want to ignore it either. In this case,
errors are exceptional and not a part of normal interaction.
In my book on Direct3D programming, I showed a technique for mapping
unexpected COM HRESULT failures to C++ exceptions so that your code
has good error detection, the error detection is pushed behind a
simple mechanism that doesn't obscure normal flow, and such errors are
immediately brought to your attention when they occur instead of the
errors accumulating behind the scenes.
See section 1.13 Code Techniques on pg. 25 of the PDF:
<
http://user.xmission.com/~legalize/book/download/01-Introduction.pdf>
>But
>if you do that everywhere the code is filled with try-catch, isnt it?
If your code is littered with try-catch blocks, then IMO you haven't
identified your error handling strategy and have approached things in
a haphazard manner.
>For example if I resize an array should I always there use try-catch to
>check an error situation in a professional code? Because its always
>possible with new vector allocation that the memory was full.
Full or fragmented and the requested size couldn't be obtained as a
single contiguous chunk. The question is: What should I do about
bad_alloc exceptions?
For many (most?) applications, the answer is "nothing, really". If
your code was written to be exception safe, then unwinding everything
all the way out to main should gracefully release all resources
acquired so far and then call std::terminate.
<
http://en.cppreference.com/w/cpp/error/terminate>
There are other applications where a reasonable strategy for handling
std::bad_alloc is to capture it somewhere within an interactive GUI
processing event loop and abort an in-progress operation cleanly,
informing the user that the requested operation could not be completed
because of insufficient memory.
>I guess the try-catch block should be put to everywhere where an
>exception can be thrown, right?
Again, the question that should be asked here is: what is my error
(exception) handling policy? Where, if anywhere, can or should I
handle unexpected exceptions? Depending on the code you are writing,
the answer can be very different.
If I am writing a library and my callers may be from C or some other
language that doesn't support C++ exceptions, then I shouldn't allow
any C++ exceptions to unwind out of any of my external API entry
points. I should catch all exceptions and turn them into some kind of
C style error code that can be inspected by the caller.
If I am writing a library intended only to be used from C++, I might
allow exceptions to unwind from my library. However, even here it is
not a rule -- even the standard library has different strategies for
different components. For instance, attempting to extract an int from
an istream that is empty doesn't result in an exception. Even
attempting to open an ifstream on a non-existent file doesn't throw an
exception.
The filesystem technical specification generally provides two forms of
operations like "copy_file": one that throws on error and another that
returns an error code on failure. This leaves the choice of the error
handling mechanism up to the caller, but either way the copy_file
operation detects errors.
<
http://en.cppreference.com/w/cpp/experimental/fs/copy_file>
My general rule of thumbs are:
- understand what, if any, error/exception handling strategy your
application should have and then look for the fewest number of places
to apply the error/exception handling. Usually that is just a small
handfull of places. If you're doing a try/catch around every
expression that could potentially throw, you're doing it wrong.
- decide on the error handling policy for all subsystems (should
exceptions throw across the library boundary, for instance?)
- turn all C style error codes into C++ exceptions when the underlying
API call shouldn't fail in the normal course of operation. Let them
break your application.
- most GUI frameworks don't like it when event handlers throw
exceptions up into the GUI framework, so consider "GUI framework
event handlers" to be living at the boundary between systems.
- bad_alloc usually indicates such catastrophic failure that there
usually isn't much you can do about it besides exit which is what will
happen anyway. However, interactive applications may want to
provide the user with one last chance to save unsaved work. This,
in turn, may also fail if it needs to allocate memory in order to
save work.
- avoid C style error status codes for most things and instead prefer
exceptions for unusual/exceptional errors
--
"The Direct3D Graphics Pipeline" free book <
http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <
http://computergraphicsmuseum.org>
The Terminals Wiki <
http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <
http://legalizeadulthood.wordpress.com>