RSS LJ

February 9, 2005

Exception handling (, )

by fluffy at 4:23 PM
The right way to handle an exception: Print out a debug message which allows the programmer to figure out where, and what, went wrong. Also, if it's not a critical error, try to put things in a consistent state.

The wrong way to handle an exception:

try {
/* some code which needs to work right or else you just get a bunch of NullPointerExceptions on every frame */
} catch (Exception e) {}
On a related note, I'm currently having trouble debugging a bunch of code which was haphazardly ported from BREW to Java by a certain someone who basically just copy-pasted the C++ code over to a Java class and fixed the syntax until it compiled. Meaning, any time the compiler complained about an uncaught exception, he just wrapped it in a bogus try/catch like above. No debug message. Hell, it'd have been better if he'd just set the whole method throws Exception so that at least the default handler would have caught it and printed the stack trace.

The way it is, though, a bunch of exceptions cause certain instantiations to fail, cascading to the point that the object which is expected to be called as a frame-render delegate is just null. With absolutely no hint as to where the initial failure occurred.

Hooray.

Comments

#4528 02/09/2005 01:50 pm no way.
cut/paste one language to port to another. I may not be the best coder out there but, just wow.
#4529 02/09/2005 01:51 pm
Yeah, while I was working on Java I ran into a lot of mysterious bugs that would have been a lot easier to find if the original exceptions hadn't been silently eaten like that.

It's way too long ago for me to trust my memory, but I recall some of that being in Sun's own java.* implementations.

These days, in all projects I work on I have a permanent breakpoint set at -[NSException raise]. It's been oh so helpful.
#4530 02/09/2005 02:01 pm
Yeah. JBuilder has an option to always breakpoint on uncaught exceptions (in the default global handler) but nothing to breakpoint on just any throw, and unfortunately the MIDP runtime has its own top-level catch (Exception e) { System.out.println(e.toString); e.printStackDump(); } which prevents JBuilder from ever even seeing the exception. Though at least it prints out the stack dump so I can go to the appropriate method and set a breakpoint on the appropriate throws.
#4531 02/09/2005 02:05 pm
Hmm. I don't remember my Java very well, but is there a method on Throwable that always gets called when it's thrown, at which you could set a breakpoint? (Throwable's constructor would work, but you might get false positives if something allocates an exception but never raises it.)
#4532 02/09/2005 02:18 pm
I was just trying that after reading your trick, but JBuilder doesn't let me set a breakpoint on Throwable.Throwable() for some reason.
#4533 02/09/2005 04:05 pm maybe helpful
Hey, I don't know if it solves what you're trying to do, but I usually did:

public method throws Exception{
//do stuff as usual
//...
if SomethingBadHappens{
throw new Exception("Helpful message about how you screwed up the system")
}

Maybe Eclipse handles these things a little better, but that's besides the point...
#4534 02/09/2005 04:11 pm oops
solution to the wrong problem.
#4536 02/09/2005 04:42 pm
Exceptions in general are a pretty stupid way to do things. I could sum up why I think so, but Raymond Chen has already done it far more eloquently:

http://weblogs.asp.net/oldnewthing/archive/2004/04/22/118161.aspx

http://weblogs.asp.net/oldnewthing/archive/2005/01/14/352949.aspx

I think the problem with the whole idea of exceptions is that it's an attempt at "something for nothing": Robust error handling without actually putting in error handling code.

News flash: Error handling still has to go somewhere. You still have to think about every failure case. Unless you just don't care, in which case your manual-error-based code will be pretty short and "elegant" too!

Exceptions are stupid.
#4537 02/09/2005 05:01 pm
I hate exceptions. Actually I should qualify that. They're great on Java because you can only return one thing from a function and no passing in by reference. So they're necessary and because they're necessary there's well defined usage patterns and I got comfortable doing it that way. In C++ I much prefer returning error codes and triggering debug statements ON the spot and correcting the problem there or failing gracefully. Especially since you're more often than not going to integrate with C code that'll do exactly that. Like we do in my company. When I first got there, the code had been adapted from an initial Java product so we had stuff like:

bool LoadFileResource(std::string const& name) :  Throws fileerror
{
      FileResource a_file(name);
     if(a_file.IsSuccessful())
      {
         // do stuff here...
         return true;
       }

      throw fileerror;
      return false;
};

So, y'see... [takes a long drag from a joint] (As I do all too often when looking at this code) In the Java version, "FileResource" throws the appropriate exceptions. But the guy who ported it to C++ stripped the exceptions. Rather than "fixing" the port of "FileResource", the guy who ported this code just wrapped the code to maintain the behavior. Okay, I could sort of deal with that ... But that's not the best part! The best part was that even though he had wrapped this call in a try/catch block... HE WAS ALSO CHECKING THE RETURN CODE!
#4538 02/09/2005 05:01 pm
I disagree with Neill (and to a lesser degree with Raymond Chen, who points out in the second article that he was not saying exceptions are bad.)

Raymond's point is that with error-return-value based code, you "only" have to check the returned error values. I put "only" in quotes because, in real app level code, nearly everything you do has a return value. If, as he claims, in an exception-based model anything can raise an exception, then in the equivalent return-value-based model, anything can return an error code. The number of error cases to deal with is the same.

Moreover, it's not harder to ignore return values than it is to ignore exceptions. It's just as easy. The compiler doesn't help you unless you turn on really draconian warnings about ignored non-void return values (which in my experience no one ever does because it produces too much noise.)

The cost of return-value-based APIs is that the return value totally fuck up the API design. You lose the natural purpose of the return value, namely to return a result. This means you can no longer compose calls together in a single expression, like "firstThing().property().biggestFoo().increment()". Instead you have to put each of those calls on a separate line, with local variables to capture each intermediate result. Instead, the real function results turn into "out" parameters, which are annoying to work with, prone to errors, and very difficult in some language like Java.

Plus, you have to follow every call with an if( ) statement to test the return value. This results in really ugly code. There are sets of macros to make this a bit less painful, involving goto's and such. Those end up being a sort of pidgin version of real exception handling.

I spent a long time in a return-value-based world (the classic Mac Toolbox) and also a long time in an exception-based world (OpenDoc and Java). I prefer exceptions. Both can let you write bad code that doesn't handle errors, but with exceptions your overall code is much, much cleaner and less prone to all sorts of other issues.
#4539 02/09/2005 05:03 pm
I think that if a language consistently implements and enforces exception handling, and the programmers are responsible enough to at least print out a debug message, exceptions can be much more powerful than error returns, particularly in cases that you want to be able to return multiple kinds of errors (after all, NULL can only mean so many things) without doing all sorts of call-by-reference stuff, which some see as impure.

Unfortunately, C++ exceptions are useless because they're not implemented consistently, and Java basically rewards programmers for doing lazy "ignore it and it will go away" error handling (like in this entry).

If Java implicitly just threw unhandled exceptions rather than forcing the programmer to handle it explicitly, this issue wouldn't happen, because lazy programmers could just pretend exceptions don't happen and the people who have to clean up after them can just go, "Oh, hey, an uncaught exception happened here."

Better yet, if Java's Throwable.Throwable() base constructor could be set to call printCallStack() (which would have been trivial for Sun to put in as, say, a static configuration variable) or if there were a default catch() block for uncaught exceptions or whatever, then lazy programming would still be okay, and there wouldn't be a need for added overhead by keeping track of the exception stack all the time.

Granted, the examples in the first article you linked to are also valid criticisms of exceptions, but the situations described seem to also involve a pretty crappy application architecture to begin with. (Not that the architecture we're dealing with right now is particularly good, of course. But hey, all of that code is error-code-based; the exceptions are all from the JRE.)

The second article basically says, "Yes, exceptions can be used well, but only if you do them right." Then he goes on to say that you have to check every line of code - well, that's not quite true, you only have to check lines of code which can generate exceptions, and if code can generate an exception, then if it were to make error returns then you'd just have to check the error returns instead.

Exceptions at least let you batch up a bunch of error returns into a single catch block.

Also, his point about seeing whether error-code-based code is bad or not doesn't really mean much — there's plenty of bad code like that in BREW Sprung and it's easy to recognize it as such, but that still doesn't make debugging it any easier, especially since (at least in my experience) you only come across that kind of code when you're trying to debug other code and trying to figure out where and why things are breaking. In many cases, Sprung error debugging led me on a wild goose chase.

Tracking down ignored exceptions in exception-based code could be way easier than tracking down ignored error returns in error-return-based code; the problem is that Java makes it easier to simply discard (as opposed to ignore) unhandled exceptions.

His final example showing how much harder it is to tell if exception-based code is good or bad doesn't make any sense to me, but that's probably because it's very specific to a single implementation of a single object in C#, and it doesn't actually do anything to handle exceptions anyway, so I don't see how it applies to exceptions as a whole.
#4540 02/09/2005 06:03 pm Ugh
That code's awful. Seems like a case for global search/replace.

My own favorite exception WTF:

try
{
    hr = WinApiCall();
     if( FAILED(hr) )
        throw(HR);

    // repeat ten times
}
catch(hr)
{
   return hr;
}


This is especially annoying in Visual Studio, as it normally displays exceptions in the debug output window even if they are caught.

Visual Studio is nice in this respect...you can have it break on exceptions even if they are caught.