tags:

views:

349

answers:

14

I'm looking for a definition of 'exceptional' in terms of unit testing or OOP principles. Several times on SO when talking about exception throwing, I've seen comments like, "Well, I wouldn't consider getting Foo from Bar to be exceptional." (Is there a trollface emoticon?)

I did some googling and an answer wasn't immediately forthcoming. Are there any good definitions, rules of thumb, or guidelines that are more objective than opinion or preference? If one person says "this is exceptional" and another says "no, it isn't", how does one resolve this dispute?


Edit: Wow, so there are a number of answers already, with comments that disagree, and they all seem to be the opinion of the answerers :P Nobody's referenced a wikipedia page or an article a snippet from a greybeard. The 'meta-answer' I'm taking from this is there is no agreed-upon rule of thumb for when to use exceptions. I feel more confident employing my own, personal, idiosyncratic rules when choosing to throw exceptions.

+2  A: 

The general rule of thumb is:

  • Use exceptions for errors that you anticipate, and that could happen.
  • Use assertions to deal with errors that could never happen.
rmx
I don't think OP is contrasting exceptions to assertions, but rather to other error-handling methods, such as returning `false`.
Steven Sudit
IMO a very bad rule of thumb. Exceptions are just another flow control mechanism with certain advantages and disadvantages. Whether it's the right choice should not be determined by its name.
Michael Borgwardt
Assertions are about debug-only runtime checks that cause immediate termination. They're a form of testing, not any sort of error handling that is used in production.
Steven Sudit
@Steven: I assumed from OPs question that he was unsure where to use exceptions - as confirmed in his edit.Also, it's my view that assertions should be left in production code as long as they do not present a performance issue. IIRC, Code Complete recommends rather than having them display a message, have them write to a log file instead.
rmx
If a test is included in release code, I would no longer call it an assertion. Not saying it's necessarily a bad idea, just that we need a different label to distinguish it from debug-only tests.
Steven Sudit
+5  A: 

It's exceptional if:

  1. It is a failure condition. AND

  2. It happens infrequently and unexpectedly. AND

  3. There is no better mechanism for reporting it.

edit

Stealing blatantly from Dan Weinreb's blog entry, which Ken posted about here, I'd like to offer the following summary of what exceptions are about.

  1. The method's contract defines how (and whether) unusual conditions (ie. failures) are signaled. If the method says something is an exception, it just is. Of course, this leaves open the question of how we should design the contract.

  2. Exceptions have the benefit of not requiring any checking by the caller, as well as naturally bubbling up until they are caught by something able to handle them. They can also contain significant detail, user-readable text and stack traces. These features make them ideal for failure cases that prevent further processing but are not predictable or common, or where explicit error-handling would be disruptive to code flow. They are especially good for errors that "should never happen" but are catastrophic in effect (such as a stack overflow).

  3. Flags, error codes, magic values (NULL, nil, INVALID_HANDLE, etc.) and other return-based mechanisms do not commandeer flow, and are therefore better suited for cases that are common and best handled locally, especially those where the failure can be worked around. As they operate by convention and not fiat, you cannot count on them to be detected and handled, except that an invalid value may be designed to cause an exception if actually used (such as an INVALID_HANDLE being used to read).

  4. When using exceptions in robust code, each method should catch unexpected exceptions and wrap them inside an exception from the contract. In other words, if your method does not promise to throw NullReferenceException, you need to catch it and rethrow it inside something more general or specific. They're called exceptions, not surprises!

Steven Sudit
Mchl
@Mchl: Sorry, I wasn't clear. Yes, it's an AND. It has to be all of the above.
Steven Sudit
This seems a reasonable comment to me. Don't know why it was downvoted.
fearofawhackplanet
This sounds like good advice, but am I going to hear this any other place than from you? I'm looking for well-accepted guidelines.
@fear: Some people abuse the system with strategic downvotes.
Steven Sudit
@user: Would you feel better if I was the world-renowned author of "Everything You Ever Wanted To Know About Exceptions But Were Afraid To Ask"?
Steven Sudit
Interesting: I now have two downvotes even though there's no suggestion anywhere that my answer is wrong. Other than pointing out an ambiguity that I've since fixed, the only complaint has been that all I did was provide the right answer, without offering the OP ammunition against co-workers. Either you can speak up and share your disagreements, or you will be disregarded as strategic downvoters (aka cheaters).
Steven Sudit
For the record, I did not downvote. Upvoting now ;)
Mchl
@Mchl: Thanks. I'm actually just fine with downvotes, so long as they are legitimate. If someone disagreed with me and said why, this would be valuable no matter which of us (if either) was correct. But a silent downvote just stinks. I've gone on record at meta in favor of requiring explanations when downvoting, while allowing anonymity.
Steven Sudit
@Steven I would. (I haven't downvoted you). I'm just looking for something 'authoritative' on exceptions -- I see a lot of conflicting opinions on SO. And to be honest, "I don't consider this 'exceptional'" really irks me :) . I would like for people to be straightforward when advancing their personal predilections, or else cite some well-accepted principles, not just state it as "The Way It's Done"
@user: The reason I made the dry joke about being a famous author is that I do realize that you would benefit from something more authoritative than "this guy I never met or even heard of had some simple rules that I just happen to agree with".
Steven Sudit
@user: Unless one of the more famous participants (Atwood, Skeet, etc.) show up here, the best I can offer you as ammunition against arbitrary claims pro and con exceptionality are those three requirements. So when someone says the condition is exceptional, you can use the checklist to break that term down into its constituent parts, which are much more amenable to agreement.
Steven Sudit
@user: Hopefully, this will let you change "I don't consider EOF to be exceptional" into "EOF is not exceptional because it's common and expected.".
Steven Sudit
+8  A: 

I disagree with the other answers about something needing to be "exceptional" to warrant an "exception". I say "exceptional" is any time you want to break the normal flow of control in the way you can by throwing an upward-propagating object.

Exceptions are a language feature. They're a means to an end. They aren't magical. Like any other feature, when you use them should be guided by when you need what they can provide, not by what they are called.

Borealid
Exceptions aren't always a language feature. For example, Structured Exception Handling in C under Windows is fundamentally an OS feature.
Steven Sudit
@Steven Sudit: It is, but without the language support to throw or catch them, they're not part of the language and thus not actually relevant. SEH is only really possible because MSVC supports __try/__catch.
DeadMG
@DeadMG: It's a nice thing that there's `__try`/`_catch`, but even if that feature was missing, SEH-style exceptions would still be thrown. And, in fact, they'd be thrown even if the program was hand-crafted in Assembler. That's what I mean about it being an OS feature; the language depends on it, but it doesn't depend on the language.
Steven Sudit
@Steven Sudit: So what? Now, they depend on assembler. Fundamentally, without a language that is capable of throwing, trying and catching, exceptions are useless. That makes exceptions dependent on a host language.
DeadMG
@DeadMG: Perhaps I'm not being clear. There is no specific support required by any language for SEH exceptions to be thrown. A simple divide-by-zero or invalid pointer will generate an exception, no matter the compiler. Support is only needed if we want to handle the exception instead of letting it terminate our process.
Steven Sudit
@Borealid: I'm not sure whether I disagree, so let me ask you the same question I asked earlier: is it exception for a file read to fail due to EOF?
Steven Sudit
@Steven: I'd argue that it depends. If you are reading lines from a file until you reach EOF, then it's not exceptional (it's also not really an error, it's just your termination condition). If you try to read a 512 byte block from a file and you reach EOF before reading 512 bytes, I'd consider that exceptional.
James McNellis
@Steven Sudit: The point I am trying to make is that an exception is a flow-of-control structure, not a philosophy. Whether *any particular event* warrants language-level exception-handling structures depends on the program being written. Also, saying "exceptions aren't always a language feature" is nitpicking; my response is referring to a programmer choosing to throw or handle an exception, not to "when should hardware raise an error flag". If a language lacks constructs for dealing with this idiom, this discussion is meaningless in that domain.
Borealid
@Borealid: Exceptions are like that old SNL skit about a product that's both a dessert topping *and* a floor wax. Exceptions are a flow control structure but there's also an implicit philosophy underlying them, and it's the latter that concerns us right now. They are error conditions that are, by default, not ignored. Left unhandled, they take the whole program down. Unlike a typical numeric result code, they are complex enough to contain text, codes and even nested exceptions. These unique features require use to make some principled decisions about when or when not to throw.
Steven Sudit
@James: Unless the file happens to be a multiple of 512, we can expect the termination condition (EOF) to also count as exceptional under your criteria. I think that, rather than throwing, a more reasonable design is to return the number of bytes actually read, as opposed to requested, where zero is a reasonable value.
Steven Sudit
@Steven: I didn't explain myself clearly, sorry. I meant the "512 bytes" as an example of reading a structured file. If I'm reading a structured file of some sort and know that either there is a 512 byte field I need to read, then if I reach EOF before reading 512 bytes, it's exceptional (the file is ill-formed). I've used this a lot in reading binary database files of various formats, with great success.
James McNellis
@James: Ok, but in this case, it would be *you* throwing the exception because a formatting rule was violated ("truncated record"), not the File object throwing it due to EOF. I'm absolutely fine with the former; it's the latter I question.
Steven Sudit
+1  A: 

IMHO, an exception should be thrown, if the further execution of program would result in fatal error or unpredictable behaviour.

Mchl
Based on this, would hitting EOF on a file read count as exceptional?
Steven Sudit
I'm not sure I follow. If you're not testing if there's anything to read before reading, it's an exceptional bug.
Mchl
@Steven, if you aren't expecting the end of the file then yes... it is exceptional.
Matthew Whited
@Matt: Last I checked, files always have an end, so we should expect an EOF whenever we read them all the way through. You could suggest that we check the length before doing the read, and this is not a bad idea, but real files can potentially be modified by other processes after such a check. That's why the non-exceptional path is to simply return the number of bytes actually read, which is 0 when we're at EOF.
Steven Sudit
@Steven, Assume that records are a fixed length so it could be assumed that the length would be a multiple of that length. If you hit the end of file on a read an exception would be thrown. This exception would indicate that the file read failed... you don't have enough information at that point to know why. It could be the network drive went down or maybe the file was corrupted on the last save. Heck it could even be that the file was not locked correctly and another application was moving the allocation pointer without your application expecting it.
Matthew Whited
(And I didn't say that the EOF didn't exist... I said it wasn't expected.)
Matthew Whited
@Matt: If you take a look around, you might be amused to find that others who are editing this page have their own views about when an EOF is exceptional, which was my intent. I wanted to show that we need a principled basis to resolve these disagreements.
Steven Sudit
@Steven, which is why this should probably be a wiki question. There is no "right" or "standard" way to do exceptions. It all depends on context and design.
Matthew Whited
@Matt: Really? I think this is one of those questions where there are ambiguous areas, but also unambiguous ones. It's absolutely correct to throw an exception on an invalid reference or mathematical error. Where it should be caught and handled -- or whether it should just be allowed to kill the process -- is a related but distinct question. On the other side, it would be wrong, always wrong for `File.Exists()` to throw when the file doesn't exist.
Steven Sudit
Do you mean for `File.Exists()` to throw or for you throwing based on the return from `File.Exists()`.... either way that goes to the expected context of the particular situation.
Matthew Whited
@Matt: The former. If I want to throw when the file doesn't exist, that's my business.
Steven Sudit
+1  A: 

Personally, I think that this kind of discussion is a pure waste of time and that "what is exceptional" is the wrong question to ask.

Exceptions are just another flow control mechanism with certain advantages (flow of control can pass multiple levels up the call stack) and disadvantages (somewhat verbose, behaviour is less localized).

Whether it's the right choice should not be determined by its name. Would we have these discussions if exceptions were simply called "bubbleups" instead?

Michael Borgwardt
I guess in this sense, this is more of a question of "How do I work in a team with fellow programmers?" ( speficially in the instance of "you shouldn't throw an exception here because this case is not exceptional" ). We wouldn't have this discussion about bubbleups :)
We'd be asking whether this error is bubble-worthy or something, but the underlying issue would remain. The big thing about exceptions is that, unlike return codes, there's no need to check them. "In Soviet Russia, return codes check you!"
Steven Sudit
@user151841 @Steven: The difference is that "is it exceptional?" discussions risk focussing on the term "exceptional" more than the actual design problem at hand and how this flow control could help solve it.
Michael Borgwardt
+1  A: 

When someone rips out the power chord, that is exceptional, most of the other situations are expected.

leppie
Hair rock exceptions are the best exceptions.
Tom
Ok, so when I cast an `object` down to an `int` and it turns out to have been a `string`, what should I do instead of throwing an exception?
Steven Sudit
@Steven Sudit: AFAIK stupidity counts towards exceptions :) Maybe that's where the term 'sanity check' comes from.
leppie
Stupidity is hardly exceptional. If anything, it's to be expected.
Steven Sudit
@Steven Sudit: Arent we talking the same thing? Sure, throw him the exception, that is 'one' way of dealing with it (and probably the preferred way).
leppie
We might be. The way I took your answer is that nothing short of hardware failures are unexpected, which would imply that we should not be throwing exceptions. Apparently, I misunderstood.
Steven Sudit
A: 

And another good rule of thumb - never use exceptions to catch conditions that you can catch in code.

For example if you have a method divides 2 variables, don't use an exception to catch divide by zero errors, instead do the necessary checks beforehand.

Bad code example:

float x;
try {
x = a / b;
{
catch (System.DivideByZeoException) {
x = 0;
}

Good code example:

if (b == 0)
return 0;

Exceptions are generally expensive.

Matt Roberts
It's really matters what the intent of the code is. If you just return a valid value for an invalid input you can be in for some big pain later.
Matthew Whited
a/0 doesn't mean zero, it is mathematically meaningless. It most likely means either bad input from the user or if b is machine generated, then b is was generated by a buggy process.
MatthewMartin
To emphasize what Matthew said, you're misusing the exception in your example. If b is ever zero, something has likely gone wrong, and returning a default value will just cover that up, and you'll be digging yourself in deeper. The correct place for the `catch` is some place where you can handle the problem halfway intelligently. In addition, defensive coding like your second example can have costs, and if an exception will almost never be thrown it might be cost-effective to rely on it.
David Thornley
I'd add that checking whether `b` is zero is insufficient. When `a` is large and `b` is tiny, we can overflow anyhow.
Steven Sudit
Fair comments all, my example wasn't perhaps so well thought out. However in the example it *might* be trivial and common that b is zero, and I've seen code that catches a divbyzeroexception and returns 0, which is expensive and unnecessary.
Matt Roberts
Agreed: even if we need a catch for overflows caused by size extremes, it's not at all unreasonable to check for an obvious cause of failure -- a zero denomination -- and fail early.
Steven Sudit
A: 

I'm not going to get into the holy war of whether exceptions ought to be used as flow control or not, but rather I'm going to focus on what an exceptional event is...

Oracle, in the context of Java, defines an exceptional event this way:

An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.

Of course, this definition applies to exceptions in general, not just in Java.

The key here is "disrupts the normal flow". In other words, the program fails in such a way that it can't complete its instructions.

A program has a defined range of functionality. When all your program's functionality is accounted for (including handling of invalid input), anything that is left over is an exception and likely a bug.

I believe there are two kinds of exceptions:

  1. Bugs, which are introduced into the program by the programmer.
  2. Uncontrollable situations, like someone else said, pulling the power plug. Of course, the program wouldn't get to throw an exception in this case, but if it could, it would. Other exceptions might be some sort of failure within the OS or network that the program relies on.

For instance: A program ought to handle invalid input from users as part of its normal flow, because users will of course give you invalid input at some point. This isn't a disruption because the program's functionality should be able to parse input and see if it's valid.

However, if a programmer somehow allows null to be passed to a method that isn't supposed to accept null, then that's an exception. The method's behavior is undefined for values of null. While it's a gray area, the reason I don't necessarily consider this invalid input like the above example is because the method has a certain spec, and by coding something into the program that passes a value violating that spec, it's a bug.

Jeff
Exceptions are a *form* of flow control, so it's not really a question of whether we should use them as flow control.
Steven Sudit
+1  A: 

Well, Exceptional programming case is one which deviates the program flow from normal stream and it might be due to:

::A h/w of s/w fault that programmer cannot handle in given situation. Programmer may be not sure what to do in those case and he left it to the user or tool/library which invokes this code. ::Even programmer might not be sure the exact environment in which his code will be used and hence it is better to leave the error handling to the one who uses the code.

So exceptional case with a program might be is to use it uncommon environment or with uncommon interactions. Again uncommon interactions and unknown environments refers to the designers point of view. So deviation from Normal is exceptional and again it is based on the point of view and context of the programmer.

Is it too round and round?:D

Madhava Gaikwad
A: 

Exception should be used for things you can't control within your program. Most of time, this means you are working with external files or an internet/database connection.

For user input, YOU should control it before you compute anything with it.

Of course there is a limit to what you can expect and you should stop before reaching the "armless person exception". We came up with this term when we were programming something with movement detector and one of our friend was over-protecting his code. So we told him: "Yeah so, did you control what happens if an armless person put his hand in front of the detector?". We got a good laugh with it :)

Wildhorn
+1  A: 

Exceptions are useful when something has gone wrong that's outside the immediate scope of the problem. They should almost never be caught close to where thrown, since if they can be satisfactorily handled there they can be satisfactorily handled without throwing anything.

One example is C++'s containers, which can throw bad_alloc if they can't get the memory they need. The people who wrote the container have no idea what should happen if the container can't get memory. Perhaps this is an expected thing, and the calling code has alternatives. Perhaps this is recoverable. Perhaps this is fatal, but how should it be logged?

Yes, it's possible to pass back error codes, but will they be used? I see lots of C memory allocations without tests for NULL, and printfs that just discard the return value. Moreover, lots of functions don't have a distinguishable error code, like negative for printf and NULL for memory allocation. Where any return value can be valid, it's necessary to find a way to return an error indication, and that leads to more complication than most programmers are willing to deal with. An exception cannot be ignored, and doesn't require lots of defensive code.

David Thornley
I'm not sure that the part about non-local handling is entirely accurate. Sure, exceptions have the advantage of bubbling up until they get caught, but sometimes the right place to catch them is as they happen. For example, if I have a method that pops up a file selection dialog and loads the file, it's the ideal place to indicate to the user that their file choice was invalid and ask them to either pick another or give up.
Steven Sudit
If the right place to catch them is as they happen, consider if you could catch the error conveniently in code. It isn't always the way to go, but it's more likely.
David Thornley
A: 

Something that should not supposed to be happen.

org.life.java
How does this not cover any and all errors?
all your base are belong to us
Amir Rachum
As Dan Weinreb points out, "whether a called function signals" and "whether the caller considers that signal an error" are quite distinct. The function doing the signaling can't know if it's an error to the caller. Knowing just the class certainly can't tell you this (like the horribly named "java.lang.Error"!). Only the caller can know for certain.
Ken
Well, the function signals that it is unable to fulfill the contract by any other means. Whether the caller cares is another matter entirely, which is why they're allowed to catch and ignore.
Steven Sudit
It's the "catch" that's the problem. Unlike conditions, with exceptions the caller doesn't have the option to continue, and the callee doesn't have the option to signal without throwing. The separation of mechanism and policy is nonexistent: if you want to signal, you kiss goodbye to your environment. The Java/C++ solution to this is "pass lots of flags" and "have lots of overloads" -- or worse (better?), "pass in a functor (or three) to control my behavior". Just because I want to signal doesn't mean I'll be unable to fulfill my function's contract. They're orthogonal issues.
Ken
+1  A: 

The best discussion of this that I've seen is in Dan Weinreb's blog: What Conditions (Exceptions) are Really About.

It's ostensibly about Common Lisp, whose condition system is like a more flexible form of exceptions, but there's almost no Lisp code and you don't need to be a Common Lisp programmer to follow the concepts.

Ken
Interesting article. I'm going to steal from it blatantly and add it to my answer.
Steven Sudit
A: 

Two main cases:

  1. When calling code has asked you to do something unreasonable.
  2. When outside dependencies have left you with nothing reasonable to do.

In the first case consider a method int IntegerDivide(int dividend, int divisor). Now, we all know we should catch conditions like dividing by zero ourselves. However, if the calling code has asked this method to divide by zero, it's too late - it's not our code that is deciding to divide by zero, it's the calling code. The only reasonable thing to do here is to throw an exception.

In the second case, consider a component that reads a file and returns some sort of computed result, and what it should do when it fails to open the file. If we had access to the user interface, then we should catch this failure and present a useful and informative message to the user. However we don't, as we're writing a component for use by other code closer to the UI. Again, the only thing we can do is throw an exception (and hope that the calling code does the right thing when it comes to informing the user).

Jon Hanna