Try Catch blocks inside or outside of functions and error handing

19,381

Solution 1

In general, an exception should only be caught if it can actually be handled.

It makes no sense to catch an exception for no purpose other than to log it. The exception is that exceptions should be caught at the "top level" so that it can be logged. All other code should allow exceptions to propagate to the code that will log them.

Solution 2

I think the best way to think about this is in terms of program state. You don't want a failed operation to damage program state. This paper describes the concept of "Exception Safety".

In general, you first need to decide what level of exception safety a function needs to guarantee. The levels are

  • Basic Guarnantee
  • Strong Guarantee
  • NoThrow Guarantee

The basic Guarantee simply means that in the face of an exception or other error, no resources are leaked, the strong guarantee says that the program state is rolled back to before the exception, and nothrow methods never throw exceptions.

I personally use exceptions when an unexpected, runtime failure occurs. Unexpected means to me that such a failure should not occur in the normal course of operations. Runtime means that the error is due to the state of some external component outside of my control, as opposed to due to logic errors on my part. I use ASSERT()'s to catch logic errors, and I use boolean return values for expected errors.

Why? ASSERT isn't compiled into release code, so I don't burden my users with error checking for my own failures. That's what unit tests and ASSERTS are for. Booleans because throwing an exception can give the wrong message. Exceptions can be expensive, too. If I throw exceptions in the normal course of application execution, then I can't use the MS Visual Studio debugger's excellent "Catch on thrown" exception feature, where I can have the debugger break a program at the point that any exception is thrown, rather than the default of only stopping at unhandled (crashing) exceptions.

To see a C++ technique for the basic Guarantee, google "RAII" (Resource Acquisition is Initialiation). It's a technique where you wrap a resource in an object whose constructor allocates the resource and whos destructor frees the resource. Since C++ exceptions unwind the stack, it guarantees that resources are freed in the face of exceptions. You can use this technique to roll back program state in the face of an exception. Just add a "Commit" method to an object, and if an object isn't committed before it is destroyed, run the "Rollback" operation that restores program state in the destructor.

Solution 3

Every "module" in an application is responsible for handling its own input parameters. Normally, you should find issues as soon as possible and not hand garbage to another part of the application and rely on them to be correct. There are exceptions though. Sometimes validating an input parameter basically needs reimplementing what the callee is supposed to do (e.g. parsing an integer) in the caller. In this case, it's usually appropriate to try the operation and see if it works or not. Moreover, there are some operations that you can't predict their success without doing them. For example, you can't reliably check if you can write to a file before writing to it: another process might immediately lock the file after your check.

Solution 4

There are no real hard and fast rules around exception handling that I have encountered, however I have a number of general rules of thumb that I like to apply.

Even if some exceptions are handled at the lower layer of your system make sure there is a catch all exception handler at the entry point of your system (e.g. When you implement a new Thread (i.e. Runnable), Servlet, MessasgeDrivenBean, server socket etc). This is often the best place to make the final decision as how your system should continue (log and retry, exit with error, roll back transaction)

Never throw an execption within a finally block, you will lose the original exception and mask the real problem with an unimportant error.

Apart from that it depends on the function that you are implementing. Are you in a loop, should the rest of the items be retried or the whole list aborted?

If you rethrow an exception avoid logging as it will just add noise to your logs.

Share:
19,381
CogitoErgoSum
Author by

CogitoErgoSum

Coming Soon.

Updated on June 06, 2022

Comments

  • CogitoErgoSum
    CogitoErgoSum almost 2 years

    This is more a general purpose programming question than language specific. I've seen several appraoches to try and catches.

    One is you do whatever preprocessing on data you need, call a function with the appropriate arguments and wrap it into a try/catch block.

    The other is to simply call a function pass the data and rely on try catches within the function, with the function returning a true/false flag if errors occured.

    Third is a combination with a try catch outside the function and inside. However if the functions try catch catches something, it throws out another exception for the try catch block outside the function to catch.

    Any thoughts on the pros/cons of these methods for error control or if there is an accepted standard? My googling ninja skills have failed me on finding accurate data on this.

  • user1066101
    user1066101 over 14 years
    +1: And, an exception can only be handled when a function contains alternative strategies for getting something done.
  • extraneon
    extraneon over 14 years
    I think that catching an exception to produce usefull logging, with more context than the initial throw, can be usefull at times. After that, rethrow it if you can't handle the exception.
  • John Saunders
    John Saunders over 14 years
    "with more information" is the key. Just logging, with no additional information, should wait to the higher level.
  • user1066101
    user1066101 over 14 years
    What's a NoThrow Guarantee? Is this a C++ thing? In Java and Python there are numerous errors that you cannot (and should) not attempt to catch.
  • user1066101
    user1066101 over 14 years
    This doesn't seem to answer the question. It seems more like a review of ways exceptions can arise.
  • mmx
    mmx over 14 years
    @S.Lott: I understood the question as if the OP wanted to know whether he should rely on another module to handle errors and return boolean success/failure values or let it throw exceptions and catch them higher in the call stack... Maybe I misunderstood it. It's quite long, and I guess it addresses a part of it, at least.
  • David Gladfelter
    David Gladfelter over 14 years
    NoThrow is an absoultely necessary guarantee for the rest of the guarantees to be possible. Destructors and deinitializers (such as C's 'Free()') need to be NoThrow, meaning they'll never throw an exception. RollBack() functions should never throw, too. If you can't clean up program state without generating new exceptions, it means that you can't guarantee that program state won't be damaged. There are three ways of creating NoThrow operations. Compose them of only nothrow operations: logically guarantee that they can't throw, or put a try-catch that swallows the exceptions.
  • David Gladfelter
    David Gladfelter over 14 years
    That said, there are three exceptions I'm aware of that you simply can't prevent all of the time. They are ThreadAbort exceptions (that's what they're called in .NET languages, there may be other equivalents in other languages), Out of Memory Exceptions, and Stack Overflow exceptions. All 3 are violations of the abstract concept of a program with limitless time, memory, and call recursion available to it. There's not much you can do about any of them. Fortunately, you can usually design a program not to get hit by those in the vast majority of cases.