views:

1139

answers:

21

My question is what do most developers prefer for error handling, Exceptions or Error Return Codes. Please be language(or language family) specific and why you prefer one over the other.

I'm asking this out of curiosity. Personally I prefer Error Return Codes since they are less explosive and don't force user code to pay the exception performance penalty if they don't want to.

update: thanks for all the answers! I must say that although I dislike the unpredictability of code flow with exceptions. The answer about return code (and their elder brother handles) do add lots of Noise to the code.

+11  A: 

I use both actually.

I use return codes if it's a known, possible error. If it's a scenario that I know can, and will happen, then there's a code that gets sent back.

Exceptions are used solely for things that I'm NOT expecting.

Stephen Wrighton
+1 for very simple way to go. Atleast Its short enough so that I can read it very quickly. :-)
ydobonmai
+1  A: 

With any decent compiler or runtime environment exceptions do not incur a significant penalty. It's more or less like a GOTO statement that jumps to the exception handler. Also, having exceptions caught by a runtime environment (like the JVM) helps isolating and fixing a bug a lot easier. I'll take a NullPointerException in Java over a segfault in C any day.

Kyle Cronin
Exceptions are extremely expensive. They have to walk the stack to find potential exception handlers. This stack walk is not cheap. If a stack trace is built, it's even more expensive, because then the entire stack *must* be parsed.
Derek Park
I'm surprised that compilers can't determine where the exception will be caught at least some of the time. Plus, the fact that an exception alters the flow of code makes it easier to identify exactly where an error occurs, in my opinion, makes up for a performance penalty.
Kyle Cronin
Call stacks can get extremely complex at runtime, and compilers generally don't do that kind of analysis. Even if they did, you'd still have to walk the stack to get a trace. You'd also still have to unwind the stack to deal with `finally` blocks and destructors for stack-allocated objects.
Derek Park
I do agree that the debugging benefits of exceptions often make up for the performance costs, though.
Derek Park
Derak Park, exception are expensive when they happen. This is the reason they should not be overused. But when they don't happen, they cost virtually nothing.
paercebal
+5  A: 

My preference (in C++ and Python) is to use exceptions. The language-provided facilities make it a well-defined process to both raise, catch and (if necessary) re-throw exceptions, making the model easy to see and use. Conceptually, it's cleaner than return codes, in that specific exceptions can be defined by their names, and have additional information accompanying them. With a return code, you're limited to just the error value (unless you want to define an ReturnStatus object or something).

Unless the code you're writing is time-critical, the overhead associated with unwinding the stack is not significant enough to worry about.

Jason Etheridge
Remember that using exceptions makes program analysis harder.
phjr
+4  A: 

Exceptions should only be returned where something happens that you were not expecting.

The other point of exceptions, historically, is that return codes are inherently proprietary, sometimes a 0 could be returned from a C function to indicate success, sometimes -1, or either of them for a fail with 1 for a success. Even when they are enumerated, enumerations can be ambiguous.

Exceptions can also provide a lot more information, and specifically spell out well 'Something Went Wrong, here's what, a stack trace and some supporting information for the context'

That being said, a well enumerated return code can be useful for a known set of outcomes, a simple 'heres n outcomes of the function, and it just ran this way'

johnc
+1  A: 

I only use exceptions, no return codes. I'm talking about Java here.

The general rule I follow is if I have a method called doFoo() then it follows that if it doesn't "do foo", as it were, then something exceptional has happened and an Exception should be thrown.

SCdF
+2  A: 

A great piece of advice I got from The Pragmatic Programmer was something along the lines of "your program should be able to perform all its main functionality without using exceptions at all".

Smashery
You're misinterpreting it. What they meant was "if your program throws exceptions in its normal flows, it's wrong". In other words "only use exceptions for exceptional things".
phjr
+1  A: 

One thing I fear about exceptions is that throwing an exception will screw up code flow. For example if you do

void foo()
{
  MyPointer* p = NULL;
  try{
    p = new PointedStuff();
    //I'm a module user and  I'm doing stuff that might throw or not

  }
  catch(...)
  {
    //should I delete the pointer?
  }
}

Or even worse what if I deleted something I shouldn't have, but got thrown to catch before I did the rest of the cleanup. Throwing put a lot of weight on the poor user IMHO.

Robert Gould
This is what the <code>finally</code> statement is for. But, alas, it is not in the C++ standard...
Thomas
In C++ you should stick to the rule of thumb "Acquire resources in construcotor and release them in destrucotor. For this particular case auto_ptr will do just prefectly.
Serge
Thomas, you're wrong. C++ has not finally because it does not need it. It has RAII instead. The solution of Serge is one solution using RAII.
paercebal
Robert, use Serge's solution, and you'll find your problem going away. Now, if you write more try/catches than throws, then (by judging your comment) perhaps you have a problem in your code. Of course, using catch(...) without a re-throw is usually bad, as it hides the error to better ignore it.
paercebal
+1  A: 

I prefer to use exceptions for error handling and return values (or parameters) as the normal result of a function. This gives an easy and consistent error-handling scheme and if done correctly it makes for much cleaner looking code.

Trent
+2  A: 

One of the big differences is that exceptions force you to handle an error, whereas error return codes can go unchecked.

Error return codes, if used heavily, can also cause very ugly code with lots of if tests similar to this form:

if(function(call) != ERROR_CODE) {
    do_right_thing();
}
else {
    handle_error();
}

Personally I prefer to use exceptions for errors that SHOULD or MUST be acted upon by the calling code, and only use error codes for "expected failings" where returning something is actually valid and possible.

Daniel Bruce
At least in C/C++ and gcc you can give a function an attribute that will generate a warning when its return value is ignored.
phjr
phjr: While I disagree with the "return error code" pattern, your comment should perhaps become a full answer. I find it interesting enough. At the very least, it did give me an useful information.
paercebal
+3  A: 

In Java, I use (in the following order):

1/ Design-by-contract (ensuring preconditions are met before trying anything that might fail). This catches most things and I return an error code for this.

2/ Returning error codes whilst processing work (and performing rollback if needed).

3/ Exceptions, but these are used only for unexpected things.

paxdiablo
Wouldn't it be slightly more correct to use assertions for contracts? If the contract is broken, there's nothing to save you.
phjr
+1  A: 

My general rule in the exception vs. return code argument:

  • Use errorcodes when you need localization/internationalization -- in .NET, you could use these errorcodes to reference a resource file which will then display the error in the appropriate language. Otherwise, use exceptions
  • Use exceptions only for errors that are really exceptional. If it's something that happens fairly often, either use a boolean or an enum errorcode.
Jon Limjap
There is no reason while you could not use an exception when you do l10n/i18n. Exceptions can contain localised information as well.
gizmo
+3  A: 

I wrote a blog post about this a while ago.

The performance overhead of throwing an exception should not play any role in your decision. If you're doing it right, after all, an exception is exceptional.

Thomas
+2  A: 

There is many reason to prefer Exceptions over return code:

  • Usually, for readibility, people try to minimize the number of return statement in a method. Doing so, exceptions prevent to do some extra work while in a incoorect state, and thus prevent to potentially damage more data.
  • Exception are generally more verbose arn more easilly extensible than return value. Assume that a method return natural number and that you use negative numbers as return code when an error occurs, if the scope of you method change and now return integers, you'll have to modify all the method calls instead of just tweaking a little bit the exception.
  • Exceptions allows more easilly to separate error handling of normal behaviour. They allows to ensure that some operations performs somehow as an atomic operation.
gizmo
+1  A: 

I have a simple set of rules:

1) Use return codes for things you expect your immediate caller to react to.

2) Use exceptions for errors that are broader in scope, and may reasonable be expected to be handled by something many levels above the caller so that awareness of the error does not have to percolate up through many layers, making code more complex.

In Java I only ever used unchecked exceptions, checked exceptions end up just being another form of return code and in my experience the duality of what might be "returned" by a method call was generally more of a hinderance than a help.

Kendall Helmstetter Gelner
+3  A: 

I dislike return codes because they cause the following pattern to mushroom throughout your code

CRetType obReturn = CODE_SUCCESS;
obReturn = CallMyFunctionWhichReturnsCodes();
if ( obReturn == CODE_BLOW_UP)
{
  // bail out
  goto FunctionExit;
}

Soon a method call consisting of 4 function calls bloats up with 12 lines of error handling.. Some of which will never happen. If and switch cases abound.

Exceptions are cleaner if you use them well... to signal exceptional events .. after which the execution path cannot continue. They are often more descriptive and informational than error codes.

If you have multiple states after a method call that should be handled differently (and are not exceptional cases), use error codes or out params. Although Personaly I've found this to be rare..

I've hunted a bit about the 'performance penalty' counterargument.. more in the C++ / COM world but in the newer languages, I think the difference isn't that much. In any case, when something blows up, performance concerns are relegated to the backburner :)

Gishu
+1  A: 

I don't find return codes to be less ugly than exceptions. With the exception, you have the try{} catch() {} finally {} where as with return codes you have if(){}. I used to fear exceptions for the reasons given in the post; you don't know if the pointer needs to be cleared, what have you. But I think you have the same problems when it comes to the return codes. You don't know the state of the parameters unless you know some details about the function/method in question.

Regardless, you have to handle the error if possible. You can just as easily let an exception propagate to the top level as ignore a return code and let the program segfault.

I do like the idea of returning a value (enumeration?) for results and an exception for an exceptional case.

Sam Hoice
+3  A: 

I use Exceptions in python in both Exceptional, and non-Exceptional circumstances.

It is often nice to be able to use an Exception to indicate the "request could not be performed", as opposed to returning an Error value. It means that you /always/ know that the return value is the right type, instead of arbitarily None or NotFoundSingleton or something. Here is a good example of where I prefer to use an exception handler instead of a conditional on the return value.

try:
    dataobj = datastore.fetch(obj_id)
except LookupError:
    # could not find object, create it.
    dataobj = datastore.create(....)

The side effect is that when a datastore.fetch(obj_id) is run, you never have to check if its return value is None, you get that error immediately for free. This is counter to the argument, "your program should be able to perform all its main functionality without using exceptions at all".

Here is another example of where exceptions are 'exceptionally' useful, in order to write code for dealing with the filesystem that isn't subject to race conditions.

# wrong way:
if os.path.exists(directory_to_remove):
    # race condition is here.
    os.path.rmdir(directory_to_remove)

# right way:
try: 
    os.path.rmdir(directory_to_remove)
except OSError:
    # directory didn't exist, good.
    pass

One system call instead of two, no race condition. This is a poor example because obviously this will fail with an OSError in more circumstances than the directory doesn't exist, but it's a 'good enough' solution for many tightly controlled situations.

Jerub
+3  A: 

I believe the return codes adds to code noise. For example, I always hated the look of COM/ATL code due to return codes. There had to be an HRESULT check for every line of code. I consider the error return code is one of the bad decisions made by architects of COM. It makes it difficult to do logical grouping of the code, thus code review becomes difficult.

I am not sure about the performance comparison when there is an explicit check for the return code every line.

ragu.pattabi
COM wad designed to be usable by languages that don't support exceptions.
Kevin
That is a good point. It makes sense to deal with error codes for scripting languages. At least VB6 hides error code details well with encapsulating them in the Err object, which somewhat helps in cleaner code.
ragu.pattabi
I disagree: VB6 only logs the last error. Combined with the infamous "on error resume next", you'll miss the source of your problem altogether, by the time you see it. Note that this is the basis of error handling in Win32 APII (see GetLastError function)
paercebal
+1  A: 

Exceptions are not for error handling, IMO. Exceptions are just that; exceptional events that you did not expect. Use with caution I say.

Error codes can be OK, but returning 404 or 200 from a method is bad, IMO. Use enums (.Net) instead, that makes the code more readable and easier to use for other developers. Also you don't have to maintain a table over numbers and descriptions.

Also; the try-catch-finally pattern is an anti-pattern in my book. Try-finally can be good, try-catch can also be good but try-catch-finally is never good. try-finally can often times be replaced by a "using" statement (IDispose pattern), which is better IMO. And Try-catch where you actually catch an exception you're able to handle is good, or if you do this:

try{
    db.UpdateAll(somevalue);
}
catch (Exception ex) {
    logger.Exception(ex, "UpdateAll method failed");
    throw;
}

So as long as you let the exception continue to bubble it's OK. Another example is this: try{ dbHasBeenUpdated = db.UpdateAll(somevalue); // true/false } catch (ConnectionException ex) { logger.Exception(ex, "Connection failed"); dbHasBeenUpdated = false; }

Here I actually handle the exception; what I do outside of the try-catch when the update method fails is another story, but I think my point has been made. :)

Why is then try-catch-finally an anti-pattern? Here's why:

try{
    db.UpdateAll(somevalue);
}
catch (Exception ex) {
    logger.Exception(ex, "UpdateAll method failed");
    throw;
}
finally {
    db.Close();
}

What happens if the db object has already been closed? A new exception is thrown and it has to be handled! This is better:

try{
    using(IDatabase db = DatabaseFactory.CreateDatabase()) {
        db.UpdateAll(somevalue);
    }
}
catch (Exception ex) {
    logger.Exception(ex, "UpdateAll method failed");
    throw;
}

Or, if the db object does not implement IDisposable do this:

try{
    try {
        IDatabase db = DatabaseFactory.CreateDatabase();
        db.UpdateAll(somevalue);
    }
    finally{
        db.Close();
    }
}
catch (DatabaseAlreadyClosedException dbClosedEx) {
    logger.Exception(dbClosedEx, "Database connection was closed already.");
}
catch (Exception ex) {
    logger.Exception(ex, "UpdateAll method failed");
    throw;
}

That's my 2 cents anyway! :)

noocyte
It will be strange if an object has .Close() but has no .Dispose()
abatishchev
I'm only using Close() as an example. Feel free to think of it as something else. As I state; the using pattern should be used (doh!) if available. This of course implies that the class implements IDisposable and as such you could call Dispose.
noocyte
+4  A: 

According to Chapter 7 titled "Exceptions" in Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, numerous rationales are given for why using exceptions over return values is necessary for OO frameworks such as C#.

Perhaps this is the most compelling reason (page 179):

"Exceptions integrate well with object-oriented languages. Object-oriented languages tend to impose constraints on member signatures that are not imposed by functions in non-OO languages. For example, in the case of constructors, operator overloads, and properties, the developer has no choice in the return value. For this reason, it is not possible to standardize on return-value-based error reporting for object-oriented frameworks. An error reporting method, such as exceptions, which is out of band of the method signature is the only option."

hurst
+18  A: 

For some languages (i.e. C++) Resources leak should not be a reason

C++ is based on RAII.

If you have code that could fail, return or throw (that is, most normal code), then you should have your pointer wrapped inside a smart pointer (assuming you have a very good reason to not have your object created on stack).

Return codes are more verbose

They are verbose, and tend to develop into something like:

if(doSomething())
{
   if(doSomethingElse())
   {
      if(doSomethingElseAgain())
      {
          // etc.
      }
      else
      {
         // react to failure of doSomethingElseAgain
      }
   }
   else
   {
      // react to failure of doSomethingElse
   }
}
else
{
   // react to failure of doSomething
}

In the end, you code is a collection of idented instructions (I saw this kind of code in production code).

This code could well be translated into:

try
{
   doSomething() ;
   doSomethingElse() ;
   doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
   // react to failure of doSomething
}
catch(const SomethingElseException & e)
{
   // react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
   // react to failure of doSomethingElseAgain
}

Which cleanly separate code and error processing, which can be a good thing.

Return codes are more brittle

If not some obscure warning from one compiler (see "phjr" 's comment), they can easily be ignored.

With the above examples, assume than someone forgets to handle its possible error (this happens...). The error is ignored when "returned", and will possibly explode later (i.e. a NULL pointer). The same problem won't happen with exception.

The error won't be ignored. Sometimes, you want it to not explode, though... So you must chose carefully.

Return Codes must sometimes be translated

Let's say we have the following functions:

  • doSomething, which can return an int called NOT_FOUND_ERROR
  • doSomethingElse, which can return a bool "false" (for failed)
  • doSomethingElseSagain, which can return an Error object (with both the __LINE__, __FILE__ and half the stack variables.
  • doTryToDoSomethingWithAllThisMess which, well... Use the above functions, and return an error code of type...

What is the type of the return of doTryToDoSomethingWithAllThisMess if one of its called functions fail ?

Return Codes are not an universal solution

Operators cannot return an error code. C++ constructors can't, too.

Return Codes means you can't chain expressions

The corollary of the above point. What if I want to write:

CMyType o = add(a, multiply(b, c)) ;

I can't, because the return value is already used (and sometimes, it can't be changed). So the return value becomes the first parameter, sent as a reference... Or not.

Exception are typed

You can send different classes for each kind of exception. Ressources exceptions (i.e. out of memory) should be light, but anything else could be as heavy as necessary (I like the Java Exception giving me the whole stack).

Each catch can then be specialized.

Don't ever use catch(...) without re-throwing

Usually, you should not hide an error. If you do not re-throw, at the very least, log the error in a file, open a messagebox, whatever...

Exception are... NUKE

The problem with exception is that overusing them will produce code full of try/catches. But the problem is elsewhere: Who try/catch his/her code using STL container? Still, those containers can send an exception.

Of course, in C++, don't ever let an exception exit a destructor.

Exception are... synchronous

Be sure to catch them before they bring out your thread on its knees, or propagate inside your Windows message loop.

The solution could be mixing them?

So I guess the solution is to throw when something should not happen. And when something can happen, then use a return code or a parameter to enable to user to react to it.

So, the only question is "what is something that should not happen?"

It depends on the contract of your function. If the function accepts a pointer, but specifies the pointer must be non-NULL, then it is ok to throw an exception when the user sends a NULL pointer (the question being, in C++, when didn't the function author use references instead of pointers, but...)

Another solution would be to show the error

Sometimes, your problem is that you don't want errors. Using exceptions or error return codes are cool, but... You want to know about it.

In my job, we use a kind of "Assert". It will, depending on the values of a configuration file, no matter the debug/release compile options:

  • log the error
  • open a messagebox with a "Hey, you have a problem"
  • open a messagebox with a "Hey, you have a problem, do you want to debug"

In both development and testing, this enable the user to pinpoint the problem exactly when it is detected, and not after (when some code cares about the return value, or inside a catch).

It is easy to add to legacy code. For example:

void doSomething(CMyObject * p, int iRandomData)
{
   // etc.
}

leads a kind of code similar to:

void doSomething(CMyObject * p, int iRandomData)
{
   if(iRandomData < 32)
   {
      MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
      return ;
   }

   if(p == NULL)
   {
      MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
      throw std::some_exception() ;
   }

   if(! p.is Ok())
   {
      MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
   }

   // etc.
}

(I have similar macros that are active only on debug).

Note that on production, the configuration file does not exist, so the client never sees the result of this macro... But it is easy to activate it when needed.

Conclusion

When you code using return codes, you're preparing yourself for failure, and hope your fortress of tests is secure enough.

When you code using exception, you know that your code can fail, and usually put counterfire catch at chosen strategic position in your code. But usually, your code is more about "what it must do" then "what I fear will happen".

But when you code at all, you must use the best tool at your disposal, and sometimes, it is "Never hide an error, and show it as soon as possible". The macro I spoke above follow this philosophy.

paercebal