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.
An application that uses exceptions is more robust than an application that uses return codes. An application that uses exceptions can also give the cleanest code, since return codes don't have to be checked after every call.
Exceptions versus assertions Use assert statements to test for conditions during development that should never be true if all your code is correct. There's no point in handling such an error by using an exception, because the error indicates that something in the code has to be fixed.
Exceptions provide the means to separate the details of what to do when something out of the ordinary happens from the main logic of a program. In traditional programming, error detection, reporting, and handling often lead to confusing spaghetti code.
An exception, in programming, is an unplanned event, such as invalid input or a loss of connectivity, that occurs while a program is executing and disrupts the flow of its instructions. Exception is a short way of saying exceptional event. In Java, exceptions exist as a class, java.
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).
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.
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.
Let's say we have the following functions:
What is the type of the return of doTryToDoSomethingWithAllThisMess if one of its called functions fail ?
Operators cannot return an error code. C++ constructors can't, too.
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.
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.
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...
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.
Be sure to catch them before they bring out your thread on its knees, or propagate inside your Windows message loop.
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...)
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:
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With