Broadly speaking, there are two ways to handle errors as they pass from layer to layer in software: throwing exceptions and returning status codes. Almost everyone agrees that exceptions are the better way to do it, but some people still prefer status returns. This article shows why exceptions are better.

The examples here are in C++, because that is the primary battlefield for this argument. Older languages like C don't have exceptions as a real possibility, and newer languages like Java, Python, Ruby, and even Perl provide facilities for exceptions, without the cultural heritage that encouraged status returns in the first place.

Clean code

Exceptions let you leave error handling code out of much of your code. Exceptions are transmitted automatically through layers that have no knowledge of them, so you can write useful code that has no error handling logic at all. This helps keep the code straightforward and usable.

For example, compare two ways of writing the same simple procedure. With status returns:

STATUS DoSomething(int a, int b)
{
    STATUS st;
    st = DoThing1(a);
    if (st != SGOOD) return st;
    st = DoThing2(b);
    if (st != SGOOD) return st;
    return SGOOD;
}

And then with exceptions:

void DoSomething(int a, int b)
{
    DoThing1(a);
    DoThing2(b);
}

Even if the first version is rewritten with macros to hide much of the status return scaffolding, it's uglier and more cluttered than with exceptions:

#define TRY(s)  { STATUS st = (s); if (st != SGOOD) return st; }

STATUS DoSomething(int a, int b)
{
    TRY(DoThing1(a));
    TRY(DoThing2(b));
    return SGOOD;
}

If the code were more complex than this, the extra noise from the error handling would be much worse. Exceptions keep the code clean.

Valuable channels

With status returns, a valuable channel of communication (the return value of the function) has been taken over for error handling. Some methods are so simple, and conceptually return a value, so human temptation takes over, and the method is written to return the value rather than a status code. "It's a simple function, it can't fail, this will be more convenient". Over time, the code grows, and the method gets larger, calling more helper functions, and pretty soon it can fail, but it has no way to express it.

So the return value is overloaded: "If it fails, it returns NULL". Now we have more than one convention in the code (probably more, because your numeric getters will return -1 if they fail), and everything still has to be checked. Your previously failsafe function now has to have all of its call sites updated to check for the new error value.

Why use a technique that begs to be subverted in the first place? Exceptions stay in the background, leaving the most useful tools for the successful cases. Functions can return values, and still have a useful way to fail.

Richer error information

Status returns are typically an integer. A few bits are reserved for flags (for example, a severity indication), and the rest are a large set of enumerated failures. This is a fairly impoverished error value. For example, suppose the failure is that a file could not be found. Which file? A status return can't convey that much information.

Other channels can be developed to carry supplemental information, but typically they are not. In a status return world, the best you can hope for is for the failure site to log a message, and then to return the status. This is simplistic: perhaps the caller knows that it is OK for a file to be missing. If the file opener logs a message, the log will be incorrect (an error will be printed when nothing is wrong). If the file opener doesn't log a message, the caller (or his caller) may have no way of getting the detail on the error needed to print a useful message.

Exceptions are instances of classes, and as such can carry as much information as they need to accomplish their task. Because they can be subclassed, different exceptions can carry different data, allowing for a very rich zoology of error messages.

As an example of the richness exceptions can express, Java defines exceptions as containing a reference to another exception, so that chains of effect can be constructed and retained as the exception moves up through layers of handling. This allows for rich diagnostic information: exception B occurred here, and was caused by exception A occurring there.

Implicit code

Status returns can't even be used with some functions. For example, constructors don't have an explicit return type, and so cannot return a status code. Destructors may not even be explicitly called, never mind that they don't have a return value. In C++, operator overloading and implicit casting (controversial though they are) are other forms of implicit function calls that cannot be checked for return values.

None of these functions can be given status returns. If you don't use exception handling, you have to either come up with some other way of marking errors within them, or pretend that they cannot fail. Simple code may be fail-safe, but code always grows, adding opportunities for failure. Without a way to express the failure, your system will only grow more error-prone and mysterious.

Sins of omission

Consider what happens in each technique when a coder slips up.

When a status return goes unchecked, a failure in the called routine will be undetected. The code will continue executing as if that operation had succeeded. There's no way to characterize what might happen at that point, but it is clear that no one will know that an error had occurred. Perhaps the code will visibly fail later on, but that could be many operations later. How will you trace the problem back to the original failure?

If an exception goes uncaught, the exception will travel upward in the call stack either to a higher catch block, or to the uppermost frame where the operating system will do something with it, usually present it to the user. This is not good behavior for the system, but it is visible. You will see an exception, you will be able to diagnose where it was thrown, and where it should have been caught, and you will be able to fix the code.

I'm not covering here the problem of failing to announce a problem (either by returning a failure code or throwing an exception), because that case is a wash for the two techniques. Both are prone to it, and both will fail in similar ways.

So for human error, it comes down to this: human error with status returns results in invisible problems, human error with exceptions results in visible problems. Which would you rather have?

Counter-arguments

Joel Spolsky has argued that status returns are better. His main argument is that exceptions "are significantly worse than gotos":

  1. They are invisible in the source code. Looking at a block of code, including functions which may or may not throw exceptions, there is no way to see which exceptions might be thrown and from where. This means that even careful code inspection doesn't reveal potential bugs.
  2. They create too many possible exit points for a function. To write correct code, you really have to think about every possible code path through your function. Every time you call a function that can raise an exception and don't catch it on the spot, you create opportunities for surprise bugs caused by functions that terminated abruptly, leaving data in an inconsistent state, or other code paths that you didn't think about.

This seems like a reasonable argument until you work out what the code would look like with status returns. We aren't arguing here whether functions should be able to fail or not, just what should happen when they do. So all of those possible exit points for a function are still possible exit points, but you have to check the status returns explicitly, and return from the function. So you've traded implicit complexity for explicit complexity, which may not be a good trade. With explicit complexity, you can't see the forest for the trees. Your code is cluttered with the explicit handling of error statuses.

When presented with this explicit complexity, programmers will strive to reduce it. They have two primary ways to do it: hide the error handling, or omit the error handling.

Hiding the error handling is what we did above with the TRY macro. This simply turns your explicit code paths back into implicit code paths, but with the annoying noise of TRY littered all over the place. This is certainly no victory for the "explicit code paths are better" argument for status returns. If you're going to use implicit code paths, at least use exceptions to create them so you have some modern tools at your disposal.

The other solution to the overwhelming explicit complexity in code paths is to simply avoid checking the error returns. Developers will convince themselves (through code inspection or system-level understanding or just plain bad logic) that a certain function always succeeds. This leads to errors going unchecked, which leads to invisible problems.

In a nutshell

Status returns are difficult to use. There are places where they are impossible to use. They hijack a useful channel of communication. For all of these reasons, it is easy and tempting to not use them. When not used, they produce silent failures in your system.

Status returns are inferior to exceptions. All modern programming systems provide tools for exception handling. Use them.

See also

Comments

[gravatar]
Santosh Dawara 11:27 AM on 15 Oct 2003

Coincidence? Yesterday I had to fix a method that trapped an exception and returned a status instead. Nasty bit of of work would'nt allow the application that was using the library to handle the exceptions on an individual merit.

[gravatar]
Mike 1:04 PM on 15 Oct 2003

Joel has great software-business/project-manager intuition and smarts, but I fear he may be lacking in the pure-technical department. He argues that this argument devolves into a religious debate, like so many other debates in our industry. In fact, he's wrong, based on your arguments, and on the fact that declared exceptions reduce the cognitive load on the programmer by eliminating the need to memorize all the return values of a given function. Not to mention, return codes never get checked anyway, so having the thread/process halt (and/or passing the offense up the call stack so a higher level error loop can handle it!) on an unchecked error is clearly the correct thing to do.

[gravatar]
Anubis 2:14 PM on 15 Oct 2003

Actually, you should have written :

STATUS DoSomething(int a, int b)
{
STATUS st;
st = DoThing1(a);
if (st != SGOOD) return st;
st = DoThing2(b);
if (st != SGOOD) return st;
return SGOOD;
}

...
STATUS st;
st = DoThing1(a);
if (st != SGOOD) cout

[gravatar]
Anubis 2:17 PM on 15 Oct 2003

Ouch... My comment was cut off :-(.

Actually, I wanted to point that you forgot the whole try/catch mess in your exception example...

Exceptions does not keep code as clean as you try to show.

[gravatar]
Osmo Suvisaari 5:27 PM on 15 Oct 2003

Which would I rather have: Invisible or visible problems?

The answer clearly depends on circumstances. Errors should be as visible as possible during debugging and testing - and invisible (if possible - and logged) when used by an end-user.

I think the general idea of exceptions is good but... To crash by default: Who did come up with such a bad idea?

[gravatar]
Andrew Eidsness 6:56 PM on 15 Oct 2003

I think that the point that's getting glossed over in this debate is the work required to undo side-effects. To take joel's suggestion (http://www.joelonsoftware.com/items/2003/10/15.html) and replace DoThing1 with CopyFiles, it becomes apparent that we can't just transitively throw an exception out of DoThing2; we also need to undo the work of DoThing1 e.g., un-copy the files. Otherwise our system will get into an unknown and therefore unrecoverable state (this would be even clearer if "CopyFiles" was "DeleteFiles").

In my opinion, the best discussion on exceptions starts with Tom Cargill's "Exception Handling: A False Sense of Security". When this article was first published (in '94) it was apparent to the C++ community that exceptions and their implications were not well understood.

The challenges he posed were eventually addressed on comp.lang.c++.moderated in the "Guru of the Week #8" thread (http://pierre.aubert.free.fr/divers/gotw/gotw008.htm). And I find Herb Sutter's _Exceptional_C++_ to be the best summary of the entire discussion.

My personal conclusion (I'm a fan of exceptions when they are properly used) is just a reinforcment of what should be standard programming technique; "functions should only do one thing".

[gravatar]
fungus 7:50 PM on 15 Oct 2003

Here's Joel's "tricky" example done with exceptions:

void InstallSoftware(int a, int b)
{
FileCopier a;
RegistrySetup b;

// If we got this far without an exception
// then everything is ok
a.success();
b.success();
}

The idea is that a can undo anything it did, and will do so if you don't call a.success();

If any exceptions are thrown while the registry is being set up then a.success() isn't called and its destructor will remove all the files it installed.

[gravatar]
fungus 7:53 PM on 15 Oct 2003

Ooops, I messed up the code, I didn't spot the input parameters:

void InstallSoftware(int a, int b)
{
FileCopier f(a);
RegistrySetup r(b);

// If we got this far without an exception
// then everything is ok
f.success();
r.success();
}

[gravatar]
Grant 9:31 PM on 15 Oct 2003

First I find it ironic that Joel mentions goto in his comments against exceptions. I have run across examples of return value error handling code that use gotos to jump to a common segement of cleanup code at the end of a function to avoid redundancy.

Second, what bugs me is that there is little consistency in error reporting methods. Does it throw an exception? Does it return zero on error? Does it return non-zero on error? Does it return negative on error? Do I need to check a global runtime variable for the error code? Do I need to call another function to get the error code? Which function should I call? Does it use setjmp/longjmp? Does it throw a structured exception?

Third, exceptions have their own traps. Don't go letting your destructors propagate exceptions. You'll probably be surprised what throws clauses do in C++. Should you catch(E e), catch(E *e) or catch(E &e)? Are you checking new's for null? Time to break out the Meyers and Sutter books...

Of course, you could also talk about checked exceptions, another hotly debated topic...

[gravatar]
Dave 4:41 AM on 16 Oct 2003

Some of the same points against status returns also apply to exceptions.

Both methods suck, frankly. At least Java makes an attempt to make exceptions better/safer.

[gravatar]
Ben 8:20 AM on 16 Oct 2003

What a glaring omission in your original comparison (under "Clean Code") -- you do not show your exception handling! This tells me everything I need to know about the depth of your argument. I completely side with Joel on this one, exceptions are like gotos, for programmers who think they can avoid the issue of errors and send them all off in one corner.

[gravatar]
Bob Balaban 9:35 AM on 16 Oct 2003

Don't forget about the performance implications, too. With status returns, you have code executing on every call (regardless of success or failure) to check the status code. With exceptions, the success case (which we hope is most of the cases) does not have that extra code.

Years ago at Lotus one of the architects did an actual benchmark of some code in 123 written first with the status technique and then with the exception technique, and claimed that the exception technique was 15% less code (he was comparing compiled code size) and maybe 5% faster.

[gravatar]
Kyle Jedrusiak 12:07 PM on 16 Oct 2003

I happen to use c# and the .NET Framework.

I can predefine my own exception objects, store any pertinent information in the object that may help me debug the code, or find a way to handle the exception gracefull.

So I really have both...I can use exceptions and return usefull info on the exception.

[gravatar]
Walker 12:18 PM on 16 Oct 2003

To Mr. 'Glaring omission':

Uh--I think you missed the point. The point is that the code with exceptions will do something on an error whether you catch the exception explicitly or not. (In this example, he doesn't, meaning it would probably be caught by the OS and the program would be shut down.) In the example with status codes, if you didn't check the return code, it will trundle merrily on its way, even if something went horribly wrong.

As to which approach is better, I'm not sure--both sides have valid points. I just wanted to point out that your comment ('This tells me everything I need to know about the depth of your argument') is by no means a deep argument in itself. All it demonstrates is that you either didn't understand or just didn't bother to really read the article.

[gravatar]
109 4:12 PM on 16 Oct 2003

Bravo! Brilliant article. I've just spent a couple of hours exchanging blog comments with my friend who got too excited by Joel's article on exceptions, using most of the arguments you use. If I read this article before, I would just give him a link.

[gravatar]
Ray Trent 6:58 PM on 16 Oct 2003

I think this reason this gets cast as a religious argument is that fundamentally the 2 mechanisms do the same thing. Both require careful use, and both can be abused easily.

There typically are 10 kinds of people, those who think in binary and those who don't... oops, throw XInappropriateMetaphor.

Anyway, I think the religious disagreement is between people that prefer explicit handling of errors vs. those that prefer implicit handling. The reason I say this is that if you're in the explicit camp, exceptions are even more bloated than status returns (both in clutter and execution overhead), which removes a significant fraction of their advantages.

I tend to live in the explicit camp, personally. But that said, it's perfectly clear to me what problem exceptions are intended to solve.

But consider this: either a piece of code knows what should be done if a function it calls fails, or it doesn't. If it does, it can, and probably should, handle the error, to avoid too much "action at a distance". If it doesn't know what should be done, there's not much point in it checking for or handling the error. Passing the buck just introduces unnecessary implementation coupling.

Of course, that just recasts the debate into terms of whether it's better to crash or fail softly when an error occurs.

[gravatar]
Mathieu Routhier 9:35 AM on 22 Oct 2003

Here's just another suggestion about returning statuses, which I think is cleaner.

STATUS DoSomething(int a, int b)
{
STATUS st=SGOOD;

if (st == SGOOD)
st = DoThing1(a);

if (st == SGOOD)
st = DoThing2(b);

return st;
}

Obviously, you could omit the first check for status.

[gravatar]
Kannan Goundan 4:39 PM on 22 Oct 2003

Kyle Jedrusiak:

What two things do you mean when you say "I really have both"? Exceptions already come with richer information (there's a section in the article about this).

[gravatar]
ogd (somewhere between dog and god) 5:16 PM on 23 Oct 2003

I'd have to say hmmm... and yes. I'd have to say that Andrew @ the top of this discussion really nailed it.
Functions should only do one thing. If they don't then proceed at your own risk.
I don't know how much code I have reviewed (and developed in the past) that has not met this criteria to the detriment of readability and maintability; and of course reusability.
Even in Joel's modified trival example, it's copyfiles() that should be rolling back what it called... hopefully copyFile()
I use both statuses and throw exceptions depending on the situation. Especially on private functions I tend catch exceptions and return statuses, and then on public functions handle statuses and throw exceptions. The assumption is that I know how my privates are used, but obviously not the publics.

[gravatar]
Brit 7:43 PM on 24 Oct 2003

First of all, exceptions and status do not solve the same problem. Under "Richer Error Information", Ned writes "perhaps the caller knows that it is OK for a file to be missing". So, you're going to throw an exception, huh? Any idea what the performance hit is for an exception? Here's a bit of code:

void ThrowExceptionFunc()
{
static int ex;
int i=0;
throw ex;
}

( loop 1 million times )
{
try
{
ThrowExceptionFunc();
}
catch( int ex )
{
}
}

Compiled with VS.NET, Release build, if you comment out "throw 1", this executes in 0.002 seconds. If, on the other hand, you throw an exception, this code takes over 4 seconds. Throwing exceptions is a performance hit.

In "More Effective C++", Scott Meyers writes "Compared to a normal function return, returning from a function by throwing an exception may be as much as three orders of magnitude slower. That's quite a hit... If, however, you've been thinking of using exceptions to indicate relatively common conditions... now would be an excellent time to think again."

So, exceptions should ONLY be thrown under exceptional conditions - not for "just thought you should know" messages.

I don't like status values either. I prefer the method of writing a small class which holds error information. You have to know something about performance to make it work efficiently, but you can impliment all kinds of great warning, error, and success messages which don't require the performance overhead of exceptions and are more useful than a status number.

[gravatar]
Macneil Shonle 11:34 AM on 25 Oct 2003

OK, one major point is being missed here: Exceptions help the problem of separation of concerns in a way that result codes can't. This is important in layered software: suppose at the bottom we have layer A; on top of it is layer B, and on top of that is layer C.

When an exception occurs in layer A it may only be of concern to layer C. For example, C may know how to handle it, while B can't. A solution without exceptions would mean the error condition will crosscut B's implementation. This is also a reason why RuntimeExceptions in Java are better, because you aren't forced to check the exception or declare it in the throws clause.

(The language extension AspectJ provides better support for exceptions and allows the "softening" of need-to-handle exceptions.)

[gravatar]
Ray Hsieh 7:03 AM on 27 Oct 2003

Grant: you should catch(E& e). Funny how some of Scott Meyer's items are still sticking to me after all these years of Java programming...

[gravatar]
scott 9:28 PM on 27 Oct 2003

Hmmm.. I don't think Joel's second argument is even valid. Returning on error from a function requires me to first identify that the error occured, and then do 'something' to return -

SetLastError();
return;

or

return x;

or

throw "Exception";

or some other mechanism of notifying the caller.

I still have an exit point wherever I would discontinue current function execution. Multiple exit points are just a prevalent in non-exceptional :) code as it is with exceptional code. Even with exceptions its possible to minimize exit points from any function with good design.

As to his first argument, I agree with Ned and one of the previous posters. If exceptions are clearly declared, meaning you are using a compiler that understands declaration of exceptions that can be thrown from any function, nothing is hidden from the programmer. I would say it is just the opposite. With clearly declared exceptions the programmer can easily see the complete list of all possible error conditions from a given function.

[gravatar]
Mike Mangino 2:37 PM on 30 Oct 2003

Why is everybody bashing goto? Goto is almost required for error handling with status codes. For instance, if we do three things, createDirectories, installFiles, updateRegistry, we have multiple things to undo on failure:

int doStuff() {
if (!createDirectories)
goto err_create_directories;
if (!installFiles())
goto err_install_files;
if (!updateRegistry)
goto err_update_registry;
return SUCCESS;

err_update_registry:
undoUpdateRegistry();
err_install_files:
undoInstallFiles();
err_create_directories:
undoCreateDirectories();
return ERROR;
}

Otherwise, how do you undo all of those things?

[gravatar]
Warren Spencer 3:25 PM on 30 Oct 2003

Exceptions are nice, but you lose "context" information. When the same method is called repeatedly from within one routine, how does the "catch" block in that routine determine which call caused the exception to be thrown?

With return status checking, you know where you are. The exception example should add the appropriate code to determine this - thereby making it as messy as the return-status-checking theory.

[gravatar]
Matt Morris 10:06 AM on 6 Nov 2003

This problem was solved a while ago in C++. See appendix e of the 3rd edition of Bjarne's book. All I can say is that I have seen the techniques work and that I personally do not find them at all onerous.

As for context, you can always do catch-add_context-rethrow. Works for me.

[gravatar]
Mikael Brockman 1:32 PM on 10 Nov 2003

It should be specified that this article deals with the merits of C++, not of return values and exceptions. Return values are great to work with in Haskell, when you understand and use monads. Of course, monads can be implemented in many other languages, but using them in C++ would be very tedious.

[gravatar]
Jason Kozak 2:37 PM on 15 Nov 2003

I'm not sure why people keep bringing up performance hits with regards to exceptions.

Yes, exceptions have a performance hit, and that's not necessarily a bad thing. If you have a function that will fail regularly enough to cause a performance hit with exceptions, then it's likely what's causing it to fail isn't very "exceptional", and status codes should be used.

Joel's points against exceptions sound rather well reasoned at first, but they aren't.

Invisible exceptions are no more problematic than invisible status codes. Just as you may not know the exceptions a function may use, you just as well may not know the convention for returning error codes, especially when the function is returning a useable value as well. It doesn't matter whether you use status codes or exceptions, neither avoids the necessity of proper documentation.

Joel's second point is almost moot in itself. As Ned explained, an exception that isn't immediately caught will propagate until it is, even if that happens to be at the OS level. If a function leaves data in an inconsistant state, then your destructors need work. In addition, the ability of exceptions to propagate between layers without introducing dependancies, mentioned by Macneil, is half the benefit of their use.


In response to Warren, yes, handling exceptions from reoccuring calls will introduce more code in situations where context is important. Even ignoring the probably necessary refactoring of said function, the additional code is no more than what you'd have with status checks, and retains the benefits of exceptions.


This isn't a religious issue, it's a technical one. One solution behaves one way, the other in a different manner. Research and evaluate the behaviour of each, and decide for yourself.

[gravatar]
Csaba Csoma 5:16 PM on 24 Nov 2003

First: I'm pro exception.
But it has it's own catch.

Try to create an exception object when the exception was "out of memory".

And try to solve Mike Mangino's goto/status example with exceptions.

Is there "one thing fits all"?

[gravatar]
Jon 3:35 PM on 7 Dec 2003

> Try to create an exception object when the exception was "out of memory".

Pre-create the exception at the beginning of the program and throw it only if the event arises.

> And try to solve Mike Mangino's goto/status example with exceptions.

Something like this would work:

void doStuff() {
try {
createDirectories;
try {
installFiles;
try {
updateRegistry;
} catch (Exception e) {
undoUpdateRegistry;
throw e;
}
} catch (Exception e) {
undoInstallFiles;
throw e;
}
} catch (Exception e) {
undoCreateDirectories;
throw e;
}
}

[gravatar]
Csaba Csoma 2:39 PM on 11 Dec 2003

> Something like this would work: [...]

Nested try/catch? Is that better, than nesting "if"?
And when you have 5-6? Can you read/debug properly?

[gravatar]
Jon 12:29 PM on 14 Dec 2003

> Nested try/catch? Is that better, than nesting "if"?

Is there anything wrong with nesting "if" statements and what has this got to do with exceptions?

> And when you have 5-6? Can you read/debug properly?

When the code is properly laid out (unlike in this web forum) you can easily see the paths of execution that might transpire.

But I think you're missing the point: yes, if you want to have several operations, each of which might fail and each of which requires clean-up if they do, you'll need a relatively complex construct. Ultimately, you should have coded the operations themselves to catch their errors and clean up so that they could be used elegantly. So you'd then have:

void InstallFiles() {
try {
// Whatever
} catch (Exception E) {
// Cleanup
}
} ... and so on, for each function, then you can just go:

InstallFiles;
UpdateRegistry;
CreateDirectories;

The benefits that exceptions provide even in this case remain numerous:

- Errors can be easily propagated to the level at which they are best dealt with

- Obscure status code values do not have to be memorised yet errors cannot be accidentally ignored

- You can write the code to deal only with specific exceptions that you might expect to happen (eg, FileNotFoundException) and yet it will still operate in the case of less-common exceptions such as MemoryFullException by passing the error on automatically

- Additional information can be tied to the Exception (eg, a FileNotFound exception can store a filename where a status code of E_FILE_NOT_FOUND cannot)

The transparency of exceptions is a benefit, not a problem. A great percentage of the lines of code in a program can fail and dealing with every single circumstance individually complicates the program significantly, making it harder to understand. It also has the effect of complicating testing to the point where it might become almost impossible.

For instance, imagine code which creates two new objects:

Object o = new Object;
Object p = new Object;

Both of these calls could fail: there might not be enough memory, the object's constructor might throw an exception, etc. If these objects are particularly liable to fail, armed with exceptions, a programmer can choose to catch the exceptions and deal with them specifically - but if the chance of the functions failing is extremely remote or there is little they can realistically do if they _do_ fail, they can ignore them at this level and allow them to be dealt with by a higher level handler.

Without exceptions, checking return values becomes complusory if reliability is to be maintained. The two simple lines above become:

Object o = malloc(sizeof(o));
if (!o)
; // Do what? Quit? That's useful..
Object p = malloc(sizeof(p));
If (!p)
; // Do something imaginative

Now imagine having to write a test module that arranges things such that the creation of Object o succeeds and Object p fails and you begin to see how explicitly dealing with all possible errors might actually decrease reliability of programs.

The argument that functions have multiple possible exit points with exceptions does not hold because in the majority of cases, functions which check return values will bail out in the case of errors anyway. Exceptions consolidate the error-handling code for all cases into "catch" handlers and in fact probably serve to decrease the number of code paths that could be taken.

[gravatar]
Lachlan B 4:02 AM on 20 Feb 2004

Just thought you might be interested in reading my latest post here. Mentioned your article.

[gravatar]
Frank 11:45 AM on 14 Sep 2006

I arrived at your page by Googling: "C++" exceptions return status method comparison

Unfortunately, your site picked up that fact and highlighted in different colored backgrounds the seached for words -- making your page irritating to read at best. DON'T DO THAT!

[gravatar]
Michael C. 3:52 AM on 16 Nov 2006

I think there are other ways of checking for errors besides using exceptions or error codes.
For example, some programmers prefer to use global state variables or functions.
( For an example, see OpenGL )

Moreover, some ( professional ) programmers don't even bother checking for errors.
( Hey it either works or it doesn't work. )
For an example of this type of coding,
see the quake 3 source code.

[gravatar]
me22 6:46 PM on 28 Jan 2007

This is really not that complex.

You use exceptions for exceptional situations. For example, out of memory rarely happens and the function allocating memory can rarely do something intelligent with it anyways, so exceptions are a win. On the other hand, lookup in a hash table can and will fail often. Much better to return a Maybe (Haskell), option (SML), past-the-end iterator (C++), null (Java), or similar instead.

Just like everything else that's a (holy) war, neither is always better. Use the one that fits better and you'll be better off than anyone that thinks one is always better.

[gravatar]
AN 2:06 PM on 2 Mar 2007

Count me as heretical -- I'm with Joel in hating exceptions.

Status codes are always going to clutter up your code with explicit error handling in a way that exceptions don't, but explicitly declaring the exit points in your code is much better than only knowing them implicitly.

As far as hijacking the return value, I don't. In C#, I return a struct: Result. You wrap the result object around your return value (if any), and it returns back all nice and strongly typed.

Result ri = obj.DoStuff();
if (ri.Error) { // propagate error }
int myValue = ri.ReturnValue;

Yes, int myValue = obj.DoStuff(); is certainly cleaner. But it's easy to miss pieces of state that need to be cleaned up if you don't handle error cases explicitly. Exceptions leave subtle time bombs that go off in your code and break the application state. Error handling gives you an explicit place where you need to clean up anything interesting that happened in your method before exiting.

[gravatar]
AN 2:07 PM on 2 Mar 2007

Update: your site doesn't like angle brackets.

That should be Result<T> and then

Result<ri> = obj.DoStuff();

[gravatar]
AN 2:23 PM on 2 Mar 2007

Sigh.

Or, rather,

Result<int> ri = obj.DoStuff();

[gravatar]
Trevor 5:11 PM on 27 Nov 2008

I almost always have 1 entry point to functions (obviously) and 1 exit point.

Exceptions allow for poor programmers to do really nasty things, it is similar to why most programming courses tell you never to use gotos, its not that gotos can't be used effectively, and same with exceptions, its just it requires a lot of thought and planning, and you making your own rules involving their use.

I do not like your first snippet, I would do it like this:

STATUS DoSomething(int a, int b)
{
STATUS st;
st = DoThing1(a);
if (st == SGOOD)
{
// only do thing 2 if thing1 is good
st = DoThing2(b);
}
return st;
}

If you were to add more "things" and needed to different things when different errors occured you'll find the status technique is a lot easier to follow.

From my experience it really depends on what I'm doing, in general status's make it harder to whip out code really fast, and error handling code is more spread out, but it does result in less buggy code. If I am writing short scripts to do work for me, exceptions are awesome, I love them! You hardly need to write error handling code because you will get a nice error message to the screen when it crashes, but if you are writing a big system that will cause a catastrophe if it crashes and doesn't have a user sitting there watching the screen then using exceptions can be really tricky, most people can't do it, and it will result in buggy code. Imagine the code that controls airplanes was written using exceptions, the thought of that scares me.

[gravatar]
Josh W 2:20 PM on 29 Jan 2009

>Why is everybody bashing goto? Goto is almost required for error handling with >status codes. For instance, if we do three things, createDirectories, installFiles, >updateRegistry, we have multiple things to undo on failure:

Absolutely not.
Create an enum and throw this enum, catch and do a switch( my_error_enum ), which by nature cascades through (unless on break), and cleanup in reverse order of the function calls.

I wouldn't hire anyone that used goto's, or returned error values. Ever.

[gravatar]
Manuel 6:47 AM on 24 Mar 2009

Hi,

i'm with Joel.

the sample should be more like:
STATUS DoSomething(int a, int b)
{
STATUS retVal = DoThing1(a);

if (retVal == SGOOD)
retVal = DoThing2(b);

return retVal;
}

This is absolutely fine, especially if someone else wants to
change or extend it in the future, because it is
obvious what to do e.g.:

STATUS DoSomething(int a, int b)
{
STATUS retVal = DoThing1(a);

//this works always!!!
DoSomethingImportant(a);

if (retVal == SGOOD)
retVal = DoThing2(b);

return retVal;
}

that was easy, wasn't it?

Now lets have a look at Exception base code:

void DoSomething(int a, int b)
{
DoThing1(a);
//this does not!!! works always
DoSomethingImportant(a);
DoThing2(b);
}

In a big Project it is absolutely standard that different people manage the same code within several years.
This has nothing to do with skillsets... but just only giving you the possibility to manage and extend
your code fast without checking the hole thing over and over again for a small change. like that.

To make an extension like that in the sample above with exception based code you will have to rethink the whole
function and why and how the functions depend on each other.

However the problem is not only about redesigning the exception based functions here to make it work, the bigger
problem is, that the person adding DoSomethingImportant(a); does not know about the other stuff and
introduces a bug that happens only sometimes depending on something totally different.
This just gets worse when you span the sample above over a hierarchy of methods.

[gravatar]
Mikael Thomsen 4:48 PM on 23 Sep 2009

I am with Ned :-)

The "DoSomethingImportant" should properly be put into a finally block.

Throwing an exception should be considered in situations where the function/code cannot complete the job it is designed for.

Return values should be used for returning data to the caller.

[gravatar]
rs 9:06 AM on 18 Mar 2010

You are not helping by highlighting google search terms, only making it more annoying until I reload the page. If I wanted the terms highlighted I would have viewed the "cached" link. I don't get why people add this crap feature to their blogs.

[gravatar]
Manuel 2:09 PM on 18 Mar 2010

@Mikael

i think it is very difficult to define generally what is meant with:
"function/code cannot complete the job it is designed for"

The mentioned STATUS return value might be a Result (a custom class) containing status and return value, which btw is a pattern that works also between process/layer boundaries.

[gravatar]
Amil 12:31 PM on 25 Dec 2010

Thanks for the guide. I think I will start using exeptions in my new project!

[gravatar]
Daniel 11:08 AM on 11 Mar 2011

I think that both are pretty much the same in terms of usefulness:

With exceptions:
*You can check them.
*If the function fails and you don't check them your program will crash and say something that the user will not understand anyway.

With error numbers and returns:
*You can check them.
*If the function fails and you don't check them your program will either:
*crash and say something that the user will not understand anyway (likely)
*crash silently (also likely)
*complete execution successfully or semi-successfully
*seriously and actively mess up something

[gravatar]
Mike 2:56 AM on 15 Nov 2011

@Josh W, your "solution" is crap. It's more complicated, slow, and very hard to read. Don't just bash goto because you found people bashing it. There are times when goto is the right choice. Deal with it! And for your information, I wouldn't work for an employer like you too!

[gravatar]
Klaus 11:01 AM on 22 May 2013

Your argument basicly boils down to the facts that your favourite compiler cannot return status info very well, and that exceptions will work somehow even if programmers are lazy. Joel Spolsky has thought one step further and says that programs by such programmers will be full of bugs, and these bugs are of a kind that are hard to find. I must say that based on my experience I fully agree with Joel. Hiding info from the programmers is a very bad idea. If the programmers don't care which library functions can fail when and how, then the program will be garbage. Also on one hand assuming that the programmers throw all required exceptions in their own code, but on the other hand ignore errors occuring in called code, is a contradiction in itself. A good programmer will do both, a bad programmer will do neither, and you want such a programmer to neither write own functions, nor write code that uses other libraries, regardless of how errors are propagated back.

Add a comment:

name
email
Ignore this:
not displayed and no spam.
Leave this empty:
www
not searched.
 
Name and either email or www are required.
Don't put anything here:
Leave this empty:
URLs auto-link and some tags are allowed: <a><b><i><p><br><pre>.