Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
(not sure if it's only a C++ thing)
Exception handling is hard to learn in C++ and is certainly not a perfect solution but in most cases (other than some specific embedded software contexts) it's certainly the better solution we currently have for exception handling.
What about the future?
Are there other known ways to handle errors that are not implemented in most languages, or are only academical research?
Put another way: are there (suposedly) known better (unperfect is ok) ways to handle errors in programming languages?
Well, there's always been return codes, errno and the like. The general problem is that these can be ignored or forgotten about by programmers who are unaware that a particular call can fail. Exceptions are frequently ignored or missed by programmers too. The difference is that if you don't catch an exception the program dies. If you don't check a return code, the program continues on operating on invalid data. Java tried to force programmers to catch all of their exceptions by creating checked exceptions, which cause a compilation error if you don't specify exactly when they can be propagated and catch them eventually. This turned out to be insanely annoying, so programmers catch the exceptions with catch(...){/* do nothing*/} (in C++ parlence) as close to their source as possible, and the result is no better than ignoring a return code.
Besides these two error techniques, some of the functional languages support the use of various monadic return types which can encapsulate both errors and return values (e.g. Scala's Either type, Option type, or a monad that lets you return an approximate answer along with failure log). The advantage to these is that the only way to work with the successful return value is to execute code inside the monad, and the monad ensures that the code isn't run if there was a failure. (It's rather complicated to explain for someone who isn't a Haskell or Scala programmer.) I haven't worked with this model so much, but I expect it would be as annoying to some people as checked exceptions are.
Basically, IMO, error checking is a matter of attitude. You have three options:
Realize you have to deal with it, accept that fact cheerfully, and take the effort to write correct error handling code. (Any of them)
Use language features that force you to deal with it, and get annoyed because you don't want to deal with it, particularly when you're sure the error will never happen. (Checked Exceptions, Monads)
Use langauge features that allow you to ignore it easily, and write unsafe code because you ignored it. (Unchecked Exceptions, Return Codes)
Get the worst of both options 2 and 3 by using language features that force you to deal with it, but deali with every error in a way that explicitly ignores it. (Checked Exceptions, Monads)
Obviously, you should try to be a #1 type programmer.
Assuming you want your code to do different things according to whether an error occurs or not, you have basically three options:
1) Make this explicit everywhere in the code (C-style error return value checking). The main perceived disadvantage is that it's verbose.
2) Use non-local control flow to separate error-handling code from the "usual path" (exceptions). The main perceived disadvantage is keeping track of all the places your code can go next, especially if documented interfaces don't always list them all. Java's experiment with checked exceptions to "deal with" the latter issue weren't entirely successful either.
3) Sit on errors until "later" (IEEE-style sticky error bits and quiet NaNs, C++ error flags on streams), and check them only when convenient for the caller. The main perceived disadvantage is that setting and clearing errors requires careful use by everyone, and also that information available at the site of the error may be lost by the time it's handled.
Take your pick. (1) looks bloated and complex, and newbies mess it up by not checking for errors properly, but each line of code is easy to reason about. (2) looks small and simple, but each line of code might cause a jump to who-knows-where, so newbies mess it up by not implementing exception guarantees properly, and everyone sometimes catches exceptions in the wrong places or not at all. (3) is great when designed well, but you never know which of several possibilities each line of code is actually doing, so in a UB-rich environment like C++ that's easy to mess up too.
I think the underlying problem is basically hard: handling errors explicitly increases the branches in your code. Handling errors quietly increases the amount of state that you need to reason about, in a particular bit of code.
Exceptions also have the "is it truly exceptional?" problem. You could prevent exceptions from causing confusing control flow, by throwing them only in cases that your entire program can't recover from. But then you can't use them for errors which are recoverable from the POV of your program but not from the POV of the subsystem, so for those cases you fall back to the disadvantages of either (1) or (3).
I can't say that it is better than exceptions, but one alternative is the way that Erlang developers implement fault tolerance, known as "Let it fail". To summarize: each task gets spawned off as a separate "process" (Erlang's term for what most people call "threads"). If a process encounters an error, it just dies and a notification is back to the controlling process, which can either ignore it or take some sort of corrective action.
This supposedly leads to less complex and more robust code, as the entire program won't crash or exit due to lack of error handling. (Note that this robustness relies on some other features of the Erlang language and run-time environment.)
Joe Armstrong's thesis, which includes a section on how he envisions fault-tolerant Erlang systems, is available for download: http://www.erlang.org/download/armstrong_thesis_2003.pdf
Common Lisp's condition system is regarded as being a powerful superset beyond what exceptions let you do.
The fundamental problem with exception handling in systems I've seen is that if routine X calls routine Y, which calls routine Z, which throws an exception, there's no clean way for Y to let its caller distinguish among a number of situations:
The call failed for some reason that Y doesn't know about, but X might; from Y's perspective, if X knows why Z failed, X should expect to recover.
The call failed for some reason that Y doesn't know about, but its failure caused Y to leave some data structures in an invalid state.
The call failed for some reason that Y does know about; from its perspective, if the caller can handle the fact that the call won't return the expected result, X should recover.
The call failed because the CPU is catching fire.
This difficulty stems, I think, from the fact that exception types are centered around the question of what went wrong--a question which in many cases is largely orthogonal to the question of what to do about it. What I would like to see would be for exceptions to include a virtual "isSatisfied" method, and an effort to swallow an exception whose isSatisfied method returns false to throw a wrapped exception whose isSatisfied method would chain the nested one. Some types of exceptions like trying to add a duplicate key to a non-corrupted dictionary would provide a parameterless AcknowledgeException() method to set isSatisfied. Other exceptions implying data corruption or other problems would require the that either an AcknowledgeCoruption() method be passed the corrupted data structure, or that the corrupt data structure be destroyed. Once a corrupt data structure is destroyed in the process of stack unwinding, the universe would be happy again.
I'm not sure what the best architecture would be, but providing a means by which exceptions can communicate the extent to which the system state is corrupt or intact would go a long way toward alleviating the problems with existing architectures.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm working on a small project for numerical integration. I've been reading topics like this one and I couldn't decide if I should throw exceptions when the user supplies bad integration limits, requests impossible precision and things like that.
Also, what if a procedure fails to converge for the provided conditions? Should I throw an exception?
Right now what I have are error codes represented in integer variables, which I pass around between many routines, but I'd like to reduce the number of variables the user must declare and whose value they must consult.
I was thinking about a scenario where exceptions are thrown by the library routines in the circumstances I mentioned above and caught by the user. Would that be acceptable? If not, what would be a good approach?
Thanks.
While the question is too broad, I will try to give some general guidance, since this is one of the topics I often talk about.
In general, exceptions are useful when something happened which really shouldn't have happened.
I can give a couple of examples. A programmatic error would be one - when a function call contract is broken, I usually advice throwing an exception rather than returning an error.
An unrecoverable error should trigger an exception, however, judging between recoverable error and non-recoverable error is not always possible at the call site. For example, an attempt to open a non-existing file is usually a recoverable error, which warrants a failure code. But sometimes, the file simply must be there, and there is nothing calling code can do when it is not - so the error becomes unrecoverable. In the latter case, calling code might want the file opening function to throw an exception rather than returning a code.
This introduces the whole topic of exception policies - functions are told if they need to throw exception or return errors.
Before C++11 exceptions were avoided in projects where performance mattered (-fno-exceptions). Now, it appears that exceptions do not impact performance (see this and this), thus there is no reason not to use them.
A paranoid, but old, approach would be: divide your program in two parts, ui and numerical library. UI could be written in any language and use exceptions. Numerical library would be c or c++ and use no exceptions. For instance (win, but doesn't matter), you could have an UI in c# with exceptions that calls an "unsafe" c++ .dll where exceptions are not used.
Alternative to exception is the classic return -1;, the caller has to check return value of every call (even with optional, caller still has to check for errors). When a serie of nested function calls is executed and an error arise in the deepest function, you would have to propagate the error all the way up: you would have to check for errors in every call you do.
With Exceptions, you use a try{} block and that handle errors inside at any call-depth. Code to handle errors appears only once and does not pollute your numerical-library (or whatever you are creating)
Use exceptions!
I was talking to a friend of mine that through my new code I didn't treat exceptions, just because I didn't know how to do it in C++. His answer surprised me: "why in the hell would you want to throw excetions?". I asked him why, but he didn't have a satisfying answer, so I googled it. One of the first pages I found was a blog entry, where the guy who posted wasn't totally against exceptions, but a religious war started in the replies: http://weblogs.asp.net/alex_papadimoulis/archive/2005/03/29/396141.aspx
Now I begin to wonder: is it that bad to throw an exception? For a student like I am, is it wrong to learn programming using exceptions as a normal thing? (When I throw exceptions, I catch them in another level of the code to treat them, most of the times). I have a code example, and I want to know what should I do:
int x;
cout << "Type an integer: ";
cin >> x;
Anything that is typed there that is not an integer will trigger an exception already, right? This exception should be treated there. But when I have a possible exception inside a class that is being used elsewhere in the program, should I make this method throw an exception so I can treat it wherever I call it or should I make it return a standard value when it has any problems?
The exception is always good, always bad, or something to be used in "exceptional" situations? Why?
The C++ iostreams classes do not, by default, use exception handling. Generally one should use exceptions for situations where an error can occur, but such errors are "unusual" and "infrequent" (such as a disk failing, the network being down, etc.). For error conditions that you do expect (such as the user providing invalid input), you should probably not use exception handling, unless the logic that needs to deal with this situation is far removed from the logic in which the situation is detected, in which case using an exception is the way to go. There is nothing wrong with using exceptions, and using exceptions where they are merited is definitely good... just avoid using them where if...else works just fine.
As to the why:
Exceptions often provide a simple, elegant way of handling unexpected errors, but:
Exception propagation, depending on the compiler and platform, may be slower than ordinary control flow, so if you can handle the situation locally, then that will be at least as fast... and you especially don't want to slow down the common, expected cases (such as invalid input). Also:
Exceptions require more type information to be present and also require code that is unrelated to throwing or catching exceptions to be "exception-safe" which is why some object to exceptions.
Question 1:
There is no reason not to use exceptions.
Whether to use exceptions or other techniques depends on the situation.
Exceptions are good to use when you can't fix the error locally.
(This should be an exceptional situation :-)
Error codes are good when you can fix the error locally.
Error codes are bad if you have a library that exposes an interface that returns an error code that needs to be manually checked. In the C world forgetting to check the error code was a significant source of problems.
Exceptions are good when you want to force the user of your interface to check for an error. If the user does not explicitly check and compensate; then the exception will propagate until either it is handled or the application exits.
Problems that come with exceptions:
If your code is a combination of C and C++. Functions with C ABI do not contain enough information for exceptions to propagate. Thus throwing an exception across a C function can (depending on the compiler and other factors) cause problems.
If you register a callback functions with a C library that uses C++ code. Then you must make sure that all exceptions are caught in the callback function and not allowed to propagate back to the C-library.
But this is true when combining any two languages. You can only use the most primitive features across language boundaries (and even that can take work if the languages do not align well). Therefore trying to use any advanced features is usually not supported.
Question 2:
C++ streams by default do not use exceptions.
This is because usually you want to handle the problem immediately.
In your example you use input as an example. In this situation you want to check the error and re-request the input.
int x;
cout << "Type an integer: ";
cin >> x;
while(!cin)
{
cin.clear();
std::cout << "Failed: What do you think an integer is? Try again: ";
cin >> x;
}
Note:
Personally I don't like the phrase 'Use exceptions in Exceptional circumstances". To me that is just to vague. vector::at() throws an exception. For me accessing off the end of an array is going to be exceptional (but for Joe student I doubt it will be exceptional (it will happen every second class as the lecturer throws a new curve ball at him/her)).
Thus I prefer the term "When the problem can not be fixed locally". A problem that can not be fixed locally is when something big has gone wrong (resource depletion (memory full throw bad_alloc()), coding error (access beyond array bounds (throw range_check()))) or any number of things that can not be fixed right there.
There is actually a legitimate reason behind why you shouldn't use exceptions.
The problem is when you throw exception across boundaries (e.g. from one DLL and try...catch it in another DLL) sometimes things can go very wrong. I got a slap on the wrist too when I used it in one of my projects.
Just google it you'll find all sorts of post on this topic, at least it was a problem few years ago when I was still a fulltime C++ developer :)
I tend to follow this convention for internal parts of my applications code:
Do not throw exceptions of your own.
Catch any exception that could be expected from your calls to other code inmediately. Treat it accordingly, for example returning an error code, or "false" with the meaning of no success.
However when designing components, I do use component-scope exceptions, like for example, if connecting to a required database (one which is set up by the installer) fails. Or when you try to call any method violating its preconditions. In general, preconditions are expressed with assertions; however, in the public layer of your component, they should be checked and an exception thrown if they are violated, otherwise the caller has a hard job finding what they are doing wrong. (An alternative is of course to use a debug version, in which failed assertions will scream at you; but this is not always practical - distributing two binaries, keeping them up to date, etc.)
In my opinion, the only general rule is to be consistent and write correct code - choose some way to handle the erroneous/exceptional situations, and follow it consistently.
I think exception handling/throwing can be boiled down to the following:
Use RAII (or using in C#, or with in Python) so your code is exception safe.
Catch exceptions if there's a chance to recover from the error, else let them bubble up.
Throw exception only if you have something useful to add.
Consider a hypothetical example where you're using a serial port communication library to communicate with an external sensor of sorts. Furthermore:
The serial port communication library reports errors by throwing exceptions.
The sensor itself can communicate certain error messages through status codes in cases when it's no longer able to acquire data, when it's functioning outside its max/min operating conditions, etc.
You're using RAII (Resource Acquisition Is Initialization), so if the serial port library throws an exception you wouldn't have to worry about releasing any acquired resources.
If the serial port library thrown an exception, you can:
Catch the exception and try to recover from it somehow, e.g. by reinitializing the library.
Catch the exception, wrap it in your own, and then throw the new exception up the call stack.
Option #1 is unlikely in most cases. And option #2 would probably not add any valuable information to what's already in the original exception.
So usually it's best to allow original exception to bubble up (this is again assuming the RAII is in place and no clean up is needed).
On the other hand, if the sensor itself reports an error code, then it makes a lot of sense to throw your own exception.
This one is also a very interesting and controversial reading on the argument:
http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx
Throw an exception if, and only if, the alternative is failure to meet a post condition or to maintain an invariant.
That advice replaces an unclear subjective decision (is it a good idea) with a technical, precise question based on design decisions (invariant and post conditions) you should already have made.
If you just blindly go and say I won't use exceptions it means you're not going to use most of the STL. That means you need to write your own classes for strings/vector etc etc ...
Plus exceptions are a much cleaner way of doing error handling (IMHO) and if you already do RAII then adding exceptions is not much harder.
Read everything in Section 14 upto section 14.2 of Bjarne Stroustrup: "C++ programming Language Third Edition"
It is the most convincing answer I have come across.
I am working on a code-base with a bunch of developers who aren't primarily Computer Science or Software Engineering (Mostly Computer Engineering)
I am looking for a good article about when exceptions should be caught and when one should try to recover from them. I found an article a while ago that I thought explained things well, but google isn't helping me find it again.
We are developing in C++. Links to articles are an acceptable form of answer, as are summaries with pointers. I'm trying to teach here, so tutorial format would be good. As would something that was written to be accessible to non-software engineers. Thanks.
Herb Sutter has an excellent article that may be useful to you. It does not answer your specific question (when/how to catch) but does give a general overview and guidelines for handling exceptional conditions.
I've copied his summary here verbatim
Distinguish between errors and
nonerrors. A failure is an error if
and only if it violates a function's
ability to meet its callees'
preconditions, to establish its own
postconditions, or to reestablish an
invariant it shares responsibility for
maintaining. Everything else is not an
error.
Ensure that errors always leave your
program in a valid state; this is the
basic guarantee. Beware of
invariant-destroying errors
(including, but not limited to,
leaks), which are just plain bugs.
Prefer to additionally guarantee that
either the final state is either the
original state (if there was an error,
the operation was rolled back) or
intended target state (if there was no
error, the operation was committed);
this is the strong guarantee.
Prefer to additionally guarantee that
the operation can never fail. Although
this is not possible for most
functions, it is required for
functions such as destructors and
deallocation functions.
Finally, prefer to use exceptions
instead of error codes to report
errors. Use error codes only when
exceptions cannot be used (when you
don't control all possible calling
code and can't guarantee it will be
written in C++ and compiled using the
same compiler and compatible compile
options), and for conditions that are
not errors.
Read the chapter "Exception Handling" from the Book
Thinking in C++, Volume 2 - Bruce Eckel
May be this MSDN section will help you...
The most simplistic advice:
If you don't know whether or not catching an exception, don't catch it and let it flow, someone will at one point.
The point about exceptions is that they are exceptional (think std::bad_alloc). Apart from some weird uses for "quick exit" of deeply nested code blocks (that I don't like much), exceptions should be used only when you happen to remark something that you have no idea how to deal with.
Let's pick examples:
file = open('littlefile.txt', open.mode.Read)
It does seem obvious, to me, that this may fail, and in a number of conditions. While reporting the cause of failure is important (for accurate diagnostic), I find that throwing an exception here is NOT good practice.
In C++ I would write such a function as:
boost::variant<FileHandle,Error> open(std::string const& name, mode_t mode);
The function may either return a file handle (great) or an error (oups). But since it's expected, better deal with it now. Also it has the great advantage of being explicit, looking at the signature means that you know what to expect (not talking about exception specifications, it's a broken feature).
In general I tend to think of these functions as find functions. When you search for something, it is expected that the search may fail, there is nothing exceptional here.
Think about the general case of an associative container:
template <typename Key, typename Value>
boost::optional<Value const&> Associative::GetItem(Key const& key) const;
Once again, thanks to Boost, I make it clear that my method may (or not) return the expected value. There is no need for a ElementNotFound exception to be thrown.
For yet another example: user input validation is expected to fail. In general, inputs are expected to be hostile / ill formed / wrong. No need for exceptions here.
On the other hand, suppose my software deal with a database and cannot possibly run without it. If the database abstraction layer loses the connection to the database and cannot establish a new one, then it makes sense to raise an exception.
I reserve exceptions for technical issues (lost connection, out of memory, etc...).
I'm having a discussion about which way to go in a new C++ project. I favor exceptions over return codes (for exceptional cases only) for the following reasons -
Constructors can't give a return code
Decouples the failure path (which should be very rare) from the logical code which is cleaner
Faster in the non-exceptional case (no checking if/else hundreds of thousands of times)
If someone screws up the return code settings (forgets to return FAIL) it can take a very long time to track down.
Better information from the message contained in the error. (It was pointed out to me that a return enum could do the same for error codes)
From Jared Par Impossible to ignore without code to specifically designed to handle it
These are the points I've come up with from thinking about it and from google searches. I must admit to being predisposed to exceptions having worked in C# for the past couple of years. Please post further reasons for using exceptions over return codes. For those who prefer return codes, I would also be willing to listen to your reasoning. Thanks
I think this article sums it up.
Arguments for Using Exceptions
Exceptions separate error-handling code from the normal program flow and thus make the code more readable, robust and extensible.
Throwing an exception is the only clean way to report an error from a constructor.
Exceptions are hard to ignore, unlike error codes.
Exceptions are easily propagated from deeply nested functions.
Exceptions can be, and often are, user defined types that carry much more information than an error code.
Exception objects are matched to the handlers by using the type system.
Arguments against Using Exceptions
Exceptions break code structure by creating multiple invisible exit points that make code hard to read and inspect.
Exceptions easily lead to resource leaks, especially in a language that has no built-in garbage collector and finally blocks.
Learning to write exception safe code is hard.
Exceptions are expensive and break the promise to pay only for what we use.
Exceptions are hard to introduce to legacy code.
Exceptions are easily abused for performing tasks that belong to normal program flow.
The best case I've heard for preferring return codes over exceptions is simply this:
Writing exception-safe code is hard [in C++].
With a great deal of recent experience in C# myself, I can empathize with your desire to use exceptions, but unfortunately C++ isn't C#, and a lot of things that we can get away with in C# can be ultimately deadly in C++.
A good summation of the case for and against can be found in Google's style guidelines. In short:
Pros:
Exceptions allow higher levels of an application to decide how to
handle "can't happen" failures in
deeply nested functions, without the
obscuring and error-prone bookkeeping
of error codes.
Exceptions are used by most other modern languages. Using them in
C++ would make it more consistent with
Python, Java, and the C++ that others
are familiar with.
Some third-party C++ libraries use exceptions, and turning them off
internally makes it harder to
integrate with those libraries.
Exceptions are the only way for a constructor to fail. We can simulate
this with a factory function or an
Init() method, but these require heap
allocation or a new "invalid" state,
respectively.
Exceptions are really handy in testing frameworks.
Cons:
When you add a throw statement to an existing function, you must
examine all of its transitive callers.
Either they must make at least the
basic exception safety guarantee, or
they must never catch the exception
and be happy with the program
terminating as a result. For instance,
if f() calls g() calls h(), and h
throws an exception that f catches, g
has to be careful or it may not clean
up properly.
More generally, exceptions make the control flow of programs difficult
to evaluate by looking at code:
functions may return in places you
don't expect. This results
maintainability and debugging
difficulties. You can minimize this
cost via some rules on how and where
exceptions can be used, but at the
cost of more that a developer needs to
know and understand.
Exception safety requires both RAII and different coding practices.
Lots of supporting machinery is needed
to make writing correct exception-safe
code easy. Further, to avoid requiring
readers to understand the entire call
graph, exception-safe code must
isolate logic that writes to
persistent state into a "commit"
phase. This will have both benefits
and costs (perhaps where you're forced
to obfuscate code to isolate the
commit). Allowing exceptions would
force us to always pay those costs
even when they're not worth it.
Turning on exceptions adds data to each binary produced, increasing
compile time (probably slightly) and
possibly increasing address space
pressure.
The availability of exceptions may encourage developers to throw them
when they are not appropriate or
recover from them when it's not safe
to do so. For example, invalid user
input should not cause exceptions to
be thrown. We would need to make the
style guide even longer to document
these restrictions!
I suggest reading through and understanding the pros and cons, then making a decision for your own project based on these. You don't have the same software that google has, so what makes sense for them may not make sense for you (which is why I omitted their conclusion).
IMHO, the #1 reason to prefer exceptions over return codes is you can't silently ignore an exception. Doing so requires at least a minimum amount of extra code.
Use exceptions for exceptional error conditions. You have some good arguments for, and I'd like to attack some arguments against.
First, the standard C++ library uses exceptions itself, all over the place. You can't use container classes or iostreams without having them present. Since a lot of the useful features are going to use them, trying to get along without them is going to present a lot of problems.
Second, it isn't hard to write exception-safe code once you've learned how to do it. It requires consistent RAII, but that's how you should write anyway. You should adopt a construct-commit approach, but that is frequently an advantage, and avoids some subtle bugs. (For example, the self-assignment problem disappears entirely with a copy-swap approach.) In my experience, exception-safe code looks better in general. It is something C++ programmers have to learn, but there's lots of things C++ programmers have to learn, and this isn't that much more.
Third, provided you limit exceptions to exceptional cases, there should be minimal effects on performance. And, as Pavel Minaev has pointed out, if you have to pass error codes back with results, there's the possibility of effects on performance, since C++ isn't set up for easy returns of multiple values.
Fourth, it is indeed difficult to make older code exception-safe. However, this is a new project.
So, I see no good reasons not to throw exceptions in exceptional circumstances, and plenty of reasons to do so.
As a general rule. when recovery is possible and expected then use return codes.
When recovery is not possible or not desired then use exceptions.
Error handling is difficult, writing clean code with and without exceptions is - difficult.
As this is a new project, you don't have to worry about making old code exception safe, however you do have to worry about writing clean clear code.
Do so by using exceptions where appropriate.
Faster in the non-exceptional case (no checking if/else hundreds of thousands of times)
In the non-exceptional case, it's a single comparison to determine that E_SUCCESS was returned.
If someone screws up the return code settings (forgets to return FAIL) it can take a very long time to track down.
If someone fails to check the exceptions, it can be difficult to notice until you actually get an exception thrown there. If you're dealing with error codes, you know just by looking at it whether they're checking for them or not.
Use what makes sense. I think both have a place. There are situations where error codes are nearly impossible to use (returning failure from a constructor, for example)
Other times, error codes are just more convenient. They're easier to deal with in cases where you expect them to happen. Exceptions are for exceptional errors - the ones that aren't supposed to happen, but might do so once in a blue moon. Error codes are a lot more convenient for errors that are expected to happen regularly, and can be handled locally. Exceptions are most useful in cases where the error must be handled further up the call stack.
Also, exceptions aren't necessarily faster in the non-exceptional case. Often, they require extra exception handling code in function prolog and epilogs which has to be executed every time the function is called, whether or not it throws an exception.
Since many others have already provided the technical reasons for using exceptions over error codes, I will give a practical one.
I work on a complex system which uses return codes instead of exceptions. Now, this is very well designed code, but I would bet that, on average, about 70% of the code in every function is error handling code. A typically function looks something like this:
long Foo( )
{
long retCode = MoveStage( );
if ( retCode != RetCode_OK )
{
logger->LogError( __LINE__, __FILE__, "some message" );
return( retCode );
}
int someConfigVar = 0;
long retCode = GetConfigVar( "SomeVarName", someConfigVar );
if ( retCode != RetCode_OK )
{
logger->LogError( __LINE__, __FILE__, "some message" );
return( retCode );
}
long retCode = DoSomething( );
if ( retCode != RetCode_OK )
{
logger->LogError( __LINE__, __FILE__, "some message" );
return( retCode );
}
// and on and on and on...
}
The code is full of this and is hard to follow. On top of that, in many places the return code is ignored completely because we know that the call will not fail. Every function returns a ret code, so what you would normally return as the output of the function has to be returned as an out parameter. Also, all of these functions just return the retCode on error, so we just bubble that damn retCode to the top if something bad happens. You cannot centralize your error handling strategy this way, it becomes a messy headache.
It is very hard to write exception safe code. A completely contrived example is :-
void Class::Method()
{
i++;
SomeCallThatMightThrow();
j++;
}
Replace i++ and j++ with any two variables, resources, states that must remain synchronous. Garbage collection saved us from having to remember to pair our new's and deletes. Ironically, old fashioned explicit return code testing saves us from having to carefully analyse every function that might throw exceptions to check that they havn't screwed with the post-conditions.
One of the things I like about C++ is that it's very easy to think how the higher-level features might be implemented in terms of C features (which are easy to understand in terms of assembly). Exceptions for C++ break this mold. To get this level of understanding I have to do a lot. Just read this and you'll spend a lot of time scratching your head before you understand it.
Furthermore, exceptions require you to have good discipline making your code exception safe, and resource leak free. This means using RAII for anything that holds a resource ..
Additionally, exceptions have been shown when I have measured them to be a many, many orders of magnitude slower compared to a simple return code.
Well then they say you should only throw in exceptional circumstances, but how do you communicate the non-exceptional, expected, often-occuring errors. Well return codes of course! :)
I don't see how the benefits are worth it.
Constructors can't give a return code
(except if you are possibly throwing exceptions in a constructor you are in for a world of hurt)
Decouples the failure path (which should be very rare) from the logical code which is cleaner
(Opens up a much wider failure path. C++ exceptions are not like C# at all. I love C# exceptions. C++ exceptions are worse than useless. Partly due to implementation, partly due to what c++ is and isn't)
Faster in the non-exceptional case (no checking if/else hundreds of thousands of times)
(Not really. You have to check the same number of errors no matter what, you just don't see where they get checked. Also, there are errors that matter and ones that don't or at least matter to your code, so you don't have to check every single thing (do you check to see if new threw an error?))
If someone screws up the return code settings (forgets to return FAIL) it can take a very long time to track down.
(I'll take a million such errors over any one of the common errors that come about with C++ exceptions)
Better information from the message contained in the error. (It was pointed out to me that a return enum could do the same for error codes)
(Well, how do you implement your error handling? That's up to you, isn't it? You can have exceptions that give no useful information or error codes that log out extemsive data on failure. In C++ they seem to never give useful info in the cases you really want them to)
From Jared Par Impossible to ignore without code to specifically designed to handle it
(Sure, but this code is usually useless code. It's like passing a law saying everyone has to have bug free programs. The people who don't don't obey such laws anyway and for the people who do they already handle errors where they should anyway)
As for reasons against:
Wanting your program to actually work at least some of the time.
performance.
Ideology. If it's an error we are not supposed to 'recover' from it. We either die with an error message, log and continue, or completely ignore it. If we could fix it it wouldn't be an error, but a feature...and if you use code like that intentionally no debugging tools in the world are going to save you.
Mostly one.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I used to work for a company where some of the lead architect/developers had mandated on various projects that assertions were not to be used, and they would routinely be removed from code and replaced with exceptions.
I feel they are extremely important in writing correct code. Can anyone suggest how such a mandate could be justified? If so, what's wrong with assertions?
We use a modified version of assert, as per JaredPar's comment, that acts like a contract. This version is compiled into the release code so there is a small size overhead, but disabled unless a diagnostics switch is set, such that performance overhead is minimized. Our assert handler in this instance can be set to disabled, silent mode (e.g. log to file), or noisy mode (e.g. display on screen with abort / ignore, where abort throws an exception).
We used automated regression testing as part of our pre-release testing, and asserts are hugely important here as they allow us to find potential internal errors that cannot be picked up at a GUI level, and may not be initially fatal at a user level. With automation, we can run the tests both with and without diagnostics, with little overhead other than the execution time, so we can also determine if the asserts are having any other side effects.
One thing to be careful of with asserts is side effects. For example, you might see something like assert(MyDatabasesIsOk()), which inadvertently corrects errors in the database. This is a bug, as asserts should never change the state of the running application.
The only really negative thing I can say about assertions is they don't run in retail code. In our team we tend to avoid assertions because of this. Instead we use contracts, which are assertions that run in both retail and debug.
The only time we use assertions now is if one of the following are true.
The assertion code has a noticable performance impact
The particular condition is not fatal
Occasionally there is a piece of code that may or may not be dead. We will add an assertion that essentially says "how did you get here." Not firing does not mean the code is indeed dead but if QA emails me and says "what does this assertion mean," we now have a repro to get to a particular piece of code (it's immediately documented of course).
assertions and exceptions are used for two different things.
Assertions are used for states that should never happen. For example, a signalton pointer should never be null and this error should be picked up during development using an assert. Handling it with an exception is alot more work for nothing.
On the other hand exceptions are used for rare states that could happen in the normal running of an application. For example using fopen and it returns a null pointer. It could happen but most times it will return a valid pointer.
Using assertions is nether wrong nor right but it comes down to personal preference as at the end of the day it is a tool to make programing easier and can be replaced by other tools.
It depends on the criticality of your system: assertions are a failfast strategy, while exceptions can be used when the system can perform some kind of recovery.
For instance, I won't use assertions in a banking application or a telecommunication system : I'd throw an exception, that will be catched upper in the call stack. There, resources can be cleaned, and the next call/transaction can be processed ; only one will be lost.
Assertions are an excellent thing, but not to be confused with parameter/return value checking. You use them in situations that you don't believe will occur, not in situations that you expect could occur.
My favourite place to use them is in code blocks that really shouldn't be reached - such as a default case in switch-statement over an enum that has a case for every possible enum value.
It's relatively common that you might extend the enum with new values, but don't update all switch-statements involving the enum, you'll want to know that as soon as possible. Failing hard and fast is the best you can wish for in such circumstances.
Granted, in those places you usually want something that breaks in production builds as well. But the principle of abort()ing under such conditions is highly recommended. A good stack trace in the debugger gives you the information to fix your bug faster than guessing around.
Is it true that an assertion exists in the debug build, but not in the release build?
If you want to verify/assert something, don't you want to do that in the release build as well as in the debug build?
The only guess is that because an exception is often non-fatal that it makes for a codebase that does not die in some odd state. The counter-point is that the fatality of an assertions points right to where the problem is, thus easy to debug.
Personally I prefer to take the risk of an assertion as I feel that it leads to more predictable code that is easier to debug.
Assertions can be left on simply by not defining NDEBUG, so that's not really an issue.
The real problem is that assertions call abort(), which instantly stops the program. This can cause problems if there is critical cleanup your program must do before it quits. Exceptions have the advantage that destructors are properly called, even if the exception is never caught.
As a result, in a case where cleanup really matters, exceptions are more appropriate. Otherwise, assertions are just fine.
We use assertions to document assumptions.
We ensure in code review that no application logic is performed in the asserts, so it is quite safe to turn them off just shortly before release.
One reason to veto assert() is that it's possible to write code that works correctly when NDEBUG is defined, but fails when NDEBUG is not defined. Or vice versa.
It's a trap that good programmers shouldn't fall into very often, but sometimes the causes can be very subtle. For example, the code in the assert() might nudge memory assignments or code positions in the executable such that a segmentation fault that would happen, does not (or vice versa).
Depending on the skill level of your team, it can be a good idea to steer them away from risky areas.
Note, throwing an exception in a destructor is undefined behaviour.