0

The main languages I use are C++ and Java.

Both languages support exception handling.

I confess, I may not actually understand exception handling, at least, I certainly don't understand why you would need it.

I want to build on this question here:

Defensive Programming vs Exception Handling?

The debate between defensive/exception handling I find very interesting.

In my experience, I have yet to encounter a situation I thought required exception handling. As I stated, I may simply not understand the concept correctly. That being said, I am at the point where I think, if you end up in the situation where you need exception handling, the code is improperly structured.

Quite a claim, no doubt, as many professional libraries do use exceptions and are surely written by people that outstrip my skill and experience many times over.

My question is: Has anybody encountered a problem where exception handling was the best possible solution?

If so, please explain in detail the problem, and why exception handling was the best solution.

  • 4
    Exception handling required? Never. Situations where exception handling is the best solution? Quite often. – Gort the Robot Apr 04 '16 at 23:11
  • Can you give an example? – bigcodeszzer Apr 04 '16 at 23:12
  • Allocating memory in a constructor. – Gort the Robot Apr 04 '16 at 23:13
  • Why is exception handling better in this case, than an if()? – bigcodeszzer Apr 04 '16 at 23:16
  • The closest thing I do to exception handling, that I found useful, is putting in an 'else' at the end of a if-else chain, and then printing and Error message. – bigcodeszzer Apr 04 '16 at 23:17
  • But at the same time, if that happens I would prefer if the compiler /stops/ the program and says, "You raised an error." So I can fix it. – bigcodeszzer Apr 04 '16 at 23:18
  • 2
    I don't want to have a separate `if` statement for every memory allocation. – Gort the Robot Apr 04 '16 at 23:57
  • Remember that exceptions vs defensive coding are also a matter of performance in certain languages. In C# exceptions are quite costly and their handling slows down the application, if performance is what you are after, defensive programming in C# is the way to go. For a different language you will need to make your own research. – Andy Apr 05 '16 at 06:56
  • 1
    Possible duplicate of [I've been told that Exceptions should only be used in exceptional cases. How do I know if my case is exceptional?](http://programmers.stackexchange.com/questions/184654/ive-been-told-that-exceptions-should-only-be-used-in-exceptional-cases-how-do) – gnat Apr 05 '16 at 08:37
  • see also [Return magic value, throw exception or return false on failure?](http://programmers.stackexchange.com/questions/159096/return-magic-value-throw-exception-or-return-false-on-failure) – gnat Apr 05 '16 at 08:37
  • My personal opinion regarding exceptions is that the clue is in the name. They should be used for dealing with exceptional circumstances, such as a network going down part-way through a data transfer, etc, or when the predicates of a method have not been met when it's called. There's a nasty tendency to see exceptions being used for jobs that normal program flow constructs can handle it better. EG a method that returns an object based on argument should return the object normally, NULL if it couldn't find a match and throw an exception if the argument was invalid. – GordonM Apr 05 '16 at 09:27
  • In a well designed language (e.g. Rust) you barely need exception handling. In C# avoiding exceptions if much harder. – CodesInChaos Apr 05 '16 at 15:56
  • [Vexing exceptions](https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/) short write-up by Eric Lippert. – Nick Alexeev Apr 05 '16 at 20:31
  • @NickAlexeev That was actually very enlightening – bigcodeszzer Apr 08 '16 at 04:37

2 Answers2

8

The debate between defensive/exception handling I find very interesting.

You seem to be implying these two concepts are at odds with each other. They aren't. In fact, one common form of defensive programming is checking a function's arguments and throwing exceptions if they're invalid.

But at the same time, if that happens I would prefer if the compiler /stops/ the program and says, "You raised an error." So I can fix it.

One of the benefits of throwing exceptions is that by default they will stop the program.


A more useful comparison would be error codes versus exceptions. These are both mechanisms for dealing with unavoidable failures. In other words, failures that are not the result of a programming error, but are something that the programmer simply cannot prevent. Common examples include running out of memory, a certain file not existing or not allowing write access, or a network request timing out.

Before exceptions, these sorts of failures were usually indicated by an error code returned by the function that may unavoidably fail. The problems with this approach are: 1) It's easy for a programmer to simply never check error codes, leaving a nearly endless list of rare and subtle bugs with completely unpredictable consequences. 2) Since it's just one integer, you don't get a lot of information about why the failure happened. 3) In many cases, the place where the potentailly failing function is called is not a place where a failure can reasonably be recovered from. This requires the error code to be "passed back up" the call stack by every single function that may call it directly or indirectly. Needless to say, this is error prone.

Exceptions were intended as a solution to these problems. When an exception is thrown, it's impossible for the programmer to accidentally ignore it; either they go out of their way to catch and handle it, or the program crashes. Exceptions also tend to contain far more information about the failure than a single integer. And you don't have to write any code to allow exceptions to propagate through a function; they just keep going up until they reach code that actually wants to deal with them.

So the best places to use exceptions are when you have an unavoidable potential failure that would be dangerous to ignore and is typically difficult or impossible to recover from at the place it happens.

As a simple and perhaps somewhat extreme example of that, consider what happens when you run out of memory. Any function that creates objects can potentially run out of memory. If you were to try and handle these errors properly with error codes, almost every single non-trivial function in your program would have to remember to check if allocation failed, check if any of the other functions its calling failed, and return the out of memory code when they do. And there's no sane way to handle the failure aside from showing an "Out of memory" error message to the user (which is typically only feasible near the top of your program). So the fact that C++ handles this via an exception saves you a tremendous amount of extremely tedious and error-prone code; you only need write the code to show that "Out of memory" error message.


Exceptions are also sometimes useful for detecting and quickly failing on logic errors. Say you write a square root function. It only works on positive numbers, but the compiler can't stop people from passing a negative number into it. You can simply ignore this problem and declare that anyone making this mistake is invoking undefined behavior (see "design by contract"). But defensive programming is often a good thing, and we'd like our API to be a bit more user-friendly than that. You can't return an error code since the square root function is expected to return a square root. You can log an error message, but that doesn't stop the program right away so the programmer may not notice it. If you throw an exception, that will immediately stop the program and display a clear error message, so that's usually a good choice. Many languages also have some kind of "assertion" mechanism that's also a good solution for this. Assertions behave very similarly to exceptions, except that you can't catch them, so they're simpler in some ways.

Ixrec
  • 27,621
  • 15
  • 80
  • 87
  • I don't know enough about the memory allocation process to really comment. But one example you discussed was file opening. In my case, I simply /always/ run functions that operate on files inside an if(file.open()) branch. If the file is in fact, not open, I view that as a serious situation the programmer should have accounted for, and should be therefore dealt with in that function, in an else, or even if(!open) before the second if() – bigcodeszzer Apr 04 '16 at 23:48
  • What you have described makes sense, but is sort of general. Part of me still thinks that if a program is returning error codes all over the place, it has been poorly designed. Could you give a example with code? This is really a paradigm question. – bigcodeszzer Apr 04 '16 at 23:51
  • Well, yes, if it's returning error codes all over the place, then it's badly designed because it should be using more exceptions =P Unfortunately this is the sort of problem that's only apparent in a larger codebase, especially the part about only being able to handle the error higher up the callstack, so I don't think any code snippet I could include here would be very persuasive. Feel free to [come to our chatroom](https://chat.stackexchange.com/rooms/21/the-whiteboard) if you think that would help. – Ixrec Apr 04 '16 at 23:52
  • Yes, come to chat as I have a good example that I am too lazy to make into an actual answer. – whatsisname Apr 04 '16 at 23:53
  • @bigcodeszzer: in the if(file.open()) example, it's not uncommon to have applications where the file open is going to fail pretty much never, but where you have to handle the error anyway. In that scenario, exceptions allow you to write code that assumes the ideal scenario, and deal with handling the error condition somewhere else. When you write that way, you can save the mental processing power of having to parse that mostly useless if statement over and over and over again every time you're dealing with that relevant code. – whatsisname Apr 04 '16 at 23:58
1

I have a simple view of this. When a function cannot return a value or a method is unable to satisfy it's post-conditions, then throw an exception. It doesn't matter if the exception is thrown 1% of the time or 15% of the time or 40% of the time. Write the cleanest code possible and worry about the cost of throwing an exception when you find a performance problem. It's much easier to write and read:

retrieve data
calculate statistics
write results

than

retrieve data
got it?
   no, return error 1
calculate statistics
have the results?
   no, return error 2
write results
all ok?
   no, return error 3

Most of the time there's nothing to be done about any of these conditions and they should be caught and logged at the top level.

The worst thing to do is fill your code with catch blocks that do nothing but log and rethrow. Leave the logging for the top level. Yeah, the stack trace will be deeper. No problem, the interesting part is still at the top.

kevin cline
  • 33,608
  • 3
  • 71
  • 142