35

I just spent a few hours reading through SO questions on the topic of when to use exceptions, and it seems like there are two camps with different point of views:

  1. Use exceptions over error codes
  2. Use error codes most of the time, and exceptions only when some catastrophic error occurs

Is this just a controversial topic with no widely accepted best practice?

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
pepsi
  • 6,785
  • 6
  • 42
  • 74
  • 3
    It would help if you mentioned something about the target platform and design space. In some cases (microcontrollers with limited RAM or real-time code like device drivers) the answer is "NEVER". – Ben Voigt Apr 10 '11 at 03:39
  • 3
    You may be interested in reading [Exceptions for Practically-Unrecoverable Conditions](http://accu.org/index.php/journals/1706) and [Boost - Error and Exception Handling](http://www.boost.org/community/error_handling.html). – jweyrich Apr 10 '11 at 03:40
  • 1
    http://www.parashift.com/c++-faq-lite/exceptions.html makes for compelling reading! – johnsyweb Apr 10 '11 at 04:32
  • 4
    if exceptions had been available earlier in C++'s life there might be more consensus now – David Heffernan Apr 10 '11 at 08:42

11 Answers11

13

As you can probably gather from the wealth of answers, there is certainly no consensus.

Semantically, exceptions and error provide the exact same functionality. Indeed they are identical in about all semantic aspects, and errors can be arbitrarily enriched much like exceptions (you don't have to use a simple code, you can use a real bundle of data!).

The only difference there is is their propagation methods:

  • errors have to be passed down manually
  • exceptions are propagated automatically

On the other hand:

  • the possibility of an error is perfectly documented in the signature
  • exceptions are silent on code inspection (read GotW #20: Code Complexity and cry) and hidden paths of execution make reasoning harder.

The very reason both solutions can appear clunky is simply that error checking is difficult. Indeed most of the code I am writing daily concerns error checking, whether technical or functional.

So what to do ?

Warning: demonstration ahead, jump over to the next section if you care only for an answer

I personally like to leverage the type system here. The typical example is the pointer-reference dichotomy: a pointer is like a reference that can be null (and reseated, but it does not matter here)

Therefore, instead of:

// Exceptions specifications are better not used in C++
// Those here are just to indicate the presence of exceptions
Object const& Container::search(Key const& key) const throw(NotFound);

I will tend to write:

Object const* Container::search(Key const& key) const;

Or better yet, using clever pointers:

Pointer<Object const> Container::search(Key const& key) const;

template <typename O>
O* Pointer<O>::operator->() const throw(Null);

template <typename O>
O& Pointer<O>::operator*() const throw(Null);

Here I find the use of exception superfluous for 2 reasons:

  • If we are searching for an object, then there not finding it is both a perfectly common occurrence and there is not much data to carry about: cause of error ? it is not there
  • The client does not necessarily consider it an error that it is not there, who am I to assume that I know her business better than she does ? Who am I to decide that there will never be a case where it won't be appropriate not to find what was asked for ?

I don't have a problem with exceptions per se, but they can make the code awkward, consider:

void noExceptions(Container const& c)
{
  Pointer<Object const> o = c.search("my-item");

  if (!o) {
    o = c.search("my-other-item");
  }

  if (!o) { return; } // nothing to be done

  // do something with o
}

And compare it with the "exception" case:

void exceptions(Container const& c)
{
  Object const* p = 0;
  try {
    p = &c.search("my-item");
  }
  catch(NotFound const&) {
    try {
      p = &c.search("my-other-item");
    }
    catch(NotFound const&) {
      return; // nothing to be done
    }
  }

  // do something with p
}

In this case, the use of exceptions does not seem appropriate :/

On the other hand:

try {
 print() << "My cute little baby " << baby.name() << " weighs " << baby.weight();
}
catch(Oupsie const&) {
  // deal
}

is certainly more appealing than:

if (!print("My cute little baby ")) { /*deal*/ }
if (!print(baby.name())) { /*deal*/ }
if (!print(" weighs ")) { /*deal*/ }
if (!print(baby.weight())) { /*deal*/ }

What is the best then ?

It depends. Like all engineering problem there is no silver bullet, it's all about concessions.

So keep 2 things in mind:

  • Error reporting is part of the API
  • APIs should be designed with ease of use in mind

If you find yourself wondering whether to use an exception or not, just try to use your API. If there is no clear cut winner, it is just that: there is no ideal solution.

Oh, and do not hesitate to refactor your API when it becomes clear that the error reporting mechanism elected at the time of crafting it is no longer appropriate. Don't be ashamed: requirements change with time, so it is normal that the API change with them.

Personally I tend to use exceptions for unrecoverable errors only: I therefore have few try/catch in my code, only in the outermost levels, to accurately log the error (love stack frames) and log a dump of the BOM as well.

This is very similar (and indeed strongly influenced) by Haskell, the code there is seggregated in two clear cut parts: while any can throw exceptions, only the IO part (the extern one) may actually catch them. Therefore, the pure part must deal with error conditions with other ways in case they are "normal".

If, however, I am faced with a problem where using an exception makes the code easier to read and more natural (which is subjective) then I use an exception :)

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    Great answer. I like the bottom line: use whichever makes the code simpler. Enough of this meaningless *exceptions in exceptional cases* mantra. – Inverse Apr 10 '11 at 16:47
  • @Inverse: like many mantra, they are made to give a good rule of thumb to beginners :) – Matthieu M. Apr 11 '11 at 06:23
10

I don't think this is a discussion which is exclusive to the C++ community, but here are two high-level guidelines which have helped me:

  1. Throw exceptions only in exceptional circumstances. It sounds obvious, but many APIs get built with exceptions being thrown about 50% of the time they are called (and a boolean return status would have been more appropriate).
  2. In your catch clauses, know when to consume exceptions, when to rethrow them as-is and when to throw a different type of exception instead. I can't give you a one-size-fits-all rule for this because it's so dependent on your application's needs, but if you take one thing away from this it should be that the silent consumption of an exception might be the worst thing your code could do. Each frame should have some knowledge of what the calling frame(s) expects when things go wrong.
Brian Kelly
  • 19,067
  • 4
  • 53
  • 55
  • 1
    I think people tend to read *exception circumstances* as *error conditions*. It should be noted that this is not required and simply means that normally exceptions aren't thrown but sometimes they are. It is also okay if they communicate something other than an error condition. – edA-qa mort-ora-y Apr 10 '11 at 04:48
  • @edA-qa Can you give an example of something other than an error conditions that is okay to be communicated with an exception? – R. Martinho Fernandes Apr 10 '11 at 15:32
  • One quick example I actually use, in a threaded program one thread is doing some calculations and the main thread might make use of them. Occasionally the one thread requests a calculation prior to it being complete, so an exception is used to break-out back to a higher handler. – edA-qa mort-ora-y Apr 10 '11 at 16:24
  • I've also used exceptions inside virtual machines and interpreters to break from a lower point in the code to the outer handler. In this the intermediaries were user defined so it was not possible to propagate a return status. – edA-qa mort-ora-y Apr 10 '11 at 16:35
8

Exceptions are easier to use than error codes, because they can be thrown by an deeply nested subroutine and intercepted only at the level where it can be handled.

Error codes need to be passed up the chain so every function that calls another has to pass the error code back to its callers.

An error code doesn't have any clear functional advantages over exceptions since an exception can bundle an error code within it. There may have been a time when error codes might be more efficient than exceptions, but I think the cost of the extra code and the difficulty in maintaining it outweighs any possible advantage there.

However, error codes exist in many applications because they are written in, or were ported from, a language that didn't have exceptions, so it makes sense to keep using a uniform approach to error handling.

Larry Watanabe
  • 10,126
  • 9
  • 43
  • 46
  • 1
    Thanks. But if error codes have no advantages over exceptions, why do I keep reading that one should only "Use exceptions for exceptional conditions"? – pepsi Apr 10 '11 at 03:22
  • @pepsi: Error codes also indicate exceptional conditions, so what's the difference? – Potatoswatter Apr 10 '11 at 03:27
  • @potatoswatter: I guess "exceptional" is a relative term. But since that quote is often used as a reason to prefer exceptions over error codes, I interpret it as "use exceptions for conditions that are more exceptional than conditions you would use error codes for" :) – pepsi Apr 10 '11 at 03:33
  • @pepsi - Exceptions are meant to represent a catastrophe. Your normal if/else flow of control simply cannot continue, your loop should terminate, your function should return, whatever. The important point is that exceptions take you out of that "all is generally well" flow and into another place entirely. – Brian Kelly Apr 10 '11 at 03:39
  • 5
    @pepsi: Indeed. Likewise, I try to distinguish *error* codes from mere *result* codes. If a function specifically performs an operation which is likely to fail, for example `connect` to a socket, failure should be indicated by a code and not an exception. Later on, if the socket unexpectedly closes while reading, I would expect that to be an exception, because the failure has a deeper cause not related to the task at hand, less likely to occur, and less related to the immediate calling code. – Potatoswatter Apr 10 '11 at 03:40
  • 3
    Frequency and error propagation should be the main deciding factors. As Potatoswatter says, if a function fails often enough that the caller must *always* handle the situation, then an error code is likely good. Whereas in the write case, often the only reasonably handler is somewhere high up in the call graph, thus propagating a return code is both difficult and may lose information that an exception could carry. – edA-qa mort-ora-y Apr 10 '11 at 04:51
  • @pepsi - I think that saying came about because there was a performance penalty associated with exceptions. But I think for almost every application, this cost is so negligible it should be ignored. My personal feeling is - never use error codes except for backwards compability. – Larry Watanabe Apr 10 '11 at 16:52
8

No, there's no consensus, though.

Edit: As you can see, from the other answers, there's no consensus -- only schools of thought, principles of design, and plans of action. No plan is perfectly suited for every situation.

Andy Finkenstadt
  • 3,547
  • 1
  • 21
  • 25
  • 6
    This is not really an answer. – Sam Miller Apr 10 '11 at 03:27
  • 1
    But it is the correct answer. There are many common notions, a few common practices, but I'd hesitate to say there are any best practices. Many people don't use exceptions at all. – edA-qa mort-ora-y Apr 10 '11 at 04:54
  • As you can see, from the other answers, there's no consensus -- only schools of thought, principles of design, and plans of action. No plan is perfectly suited for every situation. (I'll amend my answer to include this statement.) – Andy Finkenstadt Apr 10 '11 at 13:48
  • I agree with the sentiment here, but found myself unsure if this was answering the question title, or the last query in the question body, and concluded it was the later. So, right answer, but not well answered. – Steve Beedie Apr 13 '11 at 23:03
  • @Steve, there was only one question, and analysis of previous questions related to exception-versus-error-code topics. I can handle the down-vote, though. :) – Andy Finkenstadt Apr 14 '11 at 12:48
4

Even if there were a common consensus, it wouldn't mean it is valid. Your primary consideration here should be the implementation and performance costs. These are both real constraints on the project and the resulting program. In this respect, here are some things to consider.

At runtime, exceptions are more expensive to propagate than simple return values. This is usually the source of the in exceptional cases argument. If you are throwing a lot of exceptions all the time you are suffering a performance penalty. Don't take this to mean exceptions are slow, they are still implemented very efficiently, but nonetheless costlier than error codes.

Exceptions can carry more information than error codes. Something like the boost::exception library allows tagged information which can provide a wealth of useful information up the chain of the exception. Unlike a simple error code saying file not found an exception can carry the filename, the module that tried to load it, and of course the underlying error code. This type of information is very hard to propagate via error codes up the stack.

Error code propagation can be cumbersome at implementation time. Any function that doesn't want to deal with an error has to pass the value up higher. Quite often you'll find code that simply ignores the error since the programmer couldn't deal with it at the time, or he didn't want to refactor his function signatures to pass along the error.

Exception catch syntax is bulky. It is often far easier to check a return code in an if statement than having to write a catch block. If a function has to catch too many differing exceptions at different points, the meaning of the code will be lost in a sea of braces and try/catch clauses. Here is the likely root of the notion that if a function call can normally fail a return code is likely better than an exception.

Understanding clearly how exceptions work will help you in making a decision. There are performance considerations but for many projects this will be negligible. There are concerns about exception safety, you have to know where it is safe and unsafe to use exceptions. Error codes also have the same safety concerns if people start doing short returns from functions.

Understanding the background of your team can also help out; consider if you have two new C++ programmers, one from a C background and the other coming from Java. Some people are more comfortable with error codes, others with exceptions. How far you push either in each direction will impact your project and contribute to the overall quality.

In the end there is no clear answer. While there are some situations where one may clearly win over the other, it highly depends on the project. Look at your code and continue to refactor when appropriate. Sometimes you won't even have an option as to what you use. Other times it'll make no difference. It is highly project specific.

edA-qa mort-ora-y
  • 30,295
  • 39
  • 137
  • 267
  • Regarding the *cost*: error conditions have constant cost, it's the same whether or not there is an error actually. Exceptions, in good implementations, only cost *when* thrown and are otherwise free (as in beer). For infrequent issues, exceptions are thus faster than error codes. – Matthieu M. Apr 10 '11 at 11:07
  • @Matthieu, yes and no. There is a minimal cost in setting up catch statements but you are correct that the majority of the cost is only when something throws (presuming your compiler doesn't suck, and some still do). And yes, error codes do have a constant cost (and its cumbersome as I mentioned), but simple error returns are extremely cheap on many CPUS. There are lots of details here, and thus it is very important to consider on a per project basis. – edA-qa mort-ora-y Apr 10 '11 at 16:29
  • 1
    qa mort-ora-y: I know of two implementations of exceptions, and the mostly used (gcc/clang at least) is the Table Driven implementation, which has no cost for setting up catches (at run-time) but occupy extra memory (not in the regular path though) and is slower in case an exception is actually thrown. – Matthieu M. Apr 11 '11 at 06:22
  • You are correct. I double checked GCC and "zero cost" exceptions truly are zero cost. I wrote a small program that has two functions, both call another which throws an exception (non-inline but compiled with optimization). The *operative* code in both functions was exactly the same (GDB disassemble command). The one with try/catch of course had exception handling epilogue code. – edA-qa mort-ora-y Apr 11 '11 at 07:01
3

There is most certainly no consensus. At a high level, my mantra is that exceptions should be used in "exceptional cases" - that is, cases that are not a result of programmer error but of unpredictable conditions in the execution environment.

There are volumes of debate surrounding this issue. Depending on other practices in which you engage when writing programs - RAII for example - exceptions may become more or less idiomatically useful.

kqnr
  • 3,596
  • 19
  • 17
1

Exceptions are the only way to properly report errors from a constructor, so the choice is clear there. Throwing an exception forces the caller to catch it and handle it somehow. The alternative design of an empty constructor with an init() method returning an error code is error prone and can lead to objects with an unknown state.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • Unfortunately and all too often I have seen bare-bones constructors alongside "Initialize" methods that return a success or failure status. – kqnr Apr 10 '11 at 03:30
  • @chomp likewise, I too have seen such designs. I have edited my answer. – Sam Miller Apr 10 '11 at 03:36
  • I disagree. When using object pools, third-party construction (or factories), or serialization the custom object management often requires that `init` functions are used in place of constructors. So while exceptions from constructors are **preferred** to `init`, they are certainly not the *only* way. – edA-qa mort-ora-y Apr 10 '11 at 04:58
  • I don't like constructors that can fail for non-fatal reasons. A constructor should not do any work, or as little as possible. All my classes have either a trivial or empty constructor body. Initialization is either done afterwards or before creating the object. – rubenvb Apr 10 '11 at 10:02
  • 3
    @rubenvb: and I don't like to have *half-objects* in my code. @Sam: another solution is to use a builder method, this one can fail without having to resort to exceptions. – Matthieu M. Apr 10 '11 at 11:09
1

The "C++ community" has factions, each of which has its own opinion about exceptions.

Where I work in the videogame industry, exceptions are forbidden in code that runs on a videogame system. This is because exceptions consume either CPU time OR memory, neither of which a videogame system has any to spare.

Also, in videogames there are few failure cases that warrant graceful recovery; If a videogame fails, human lives or property are not typically at stake.

  • I think we need a different specifier here for what you call the faction of the "C++ community" which is not using exceptions. C++ without exceptions is not really C++ but something entirely different. I would not want to work in such an environment (which you described where exceptions are forbidden) in the same way that I would reject writing a complex application in assembly code. –  Oct 22 '13 at 21:08
1

I like Boost's Error and Exception Handling guidelines. Basically, use exceptions when stack unwinding is desirable. This is simple and unambiguous.

Ben
  • 1,298
  • 11
  • 12
1

No consensus, but as the replies indicate, many people hold to the view to throw exceptinos only in exceptional circumstances. I don't much like that advice, because then the question becomes "what is an exceptional circumstance?". I prefer the following advice

A function must throw an exception if, and only if, the alternative would be failure to meet a post-condition (including all invariants as implicit post-conditions).

The implementation decision of whether to write throw in your code is then tied to the design decisions of the post-conditions for the function.

See C++ Coding Standards by Herb Sutter and Andrei Alexandrescu, items 70 (Distinguish between errors and non-errors) and 72 (Prefer to use exceptions to report errors).

Raedwald
  • 46,613
  • 43
  • 151
  • 237
0

I did not see this addressed in any other replies, but (as described in the "Effective Java" book) when doing OOP, a possiblity to skip throwing exceptions is to have a helper method in an object to "ask" whether the operation to be done is possible.

For example the hasNext() method on Iterator or Scanner which is usually called before calling the corresponding next*() method.

obaqueiro
  • 982
  • 12
  • 29