开发者

Why use exception instead of returning error code [duplicate]

开发者 https://www.devze.com 2023-01-19 00:52 出处:网络
This question already has answers here: Clos开发者_运维知识库ed 12 years ago. Possible Duplicate:
This question already has answers here: Clos开发者_运维知识库ed 12 years ago.

Possible Duplicate:

Exceptions or error codes

Hi,

I am looking for some guidelines on when to use return values v/s exceptions.

Many thanks in advance.


Errors are often detected at a very low level in your code, but handled at a very high level. If you use return codes for these, you have to design all the intermediate levels to expect the low level code and propagate it up until something handles it. With exceptions, you only need to throw at the low level, and catch at the high level (if at all). As long as your intermediate level code uses RAII, you don't have to change any of that to do the error propagation.

So I often think about the type of error and where it's likely to be handled. If the caller is likely to handle the problem, because it's expected to be a common thing, then a return code is great. If the error is something catastrophic (cannot allocate a necessary resource), then the immediate caller cannot be expected to handle it.

Other things to consider: Exceptions cannot be ignored, return codes can. Use an exception when it would be dangerous for the problem to go unhandled.

Technically, exceptions cannot propagate back up through C code. (It works on many systems, but it's not portable nor guaranteed.) So if you've got a C library calling back into your C++ code, you probably shouldn't throw an exception from the callback. Likewise for thread boundaries, etc.


Some of this might repeat content, but here are simple hints as to use one or the other:

No proper sentinel value

Your function might not be able to use a sentinel value to signal an error because all possible values are used by the function as valid answers. This may happen in different situations, most notably integer numerical algorithms. 0 and -1 are often used as special values but some fairly common algorithms, including pow might not be able to use those (i.e. 0^1=0 and -1^3=-1). Therefore, choosing proper values becomes somewhat of an art, consistancy is an issue and users have to remember the error value for each and every special case.

Some APIs recognize this and (almost) never use a real return value but consistently rely on return-by-reference semantics and use the return value in all functions as a status code, being either some (standard) "success" value, or a function-specific error code. CUDA, for instance, has such a convention.

Propagate by default

Error codes are often stated to be "more efficient" (a comment in one of the other answers explains why this is not necessarily true). However, they suffer from 2 common problems.

  1. You have to manually propagate the error up the call stack. This is often omitted (especially in example code and books, which is very irritating) because it litters the code with tiresome and error-prone handling code.
  2. Erros are diluted at high levels because error codes between different APIs are often impossible to concialiate. Moreover, designing you own error codes that cover the union of all libraries' error codes is a herculian task.This is why, in many applications, you get an The operation failed. message instead of Ran out of disk space..

To address (1), exceptions are propagated by default. Intentionally ignoring an error becomes obvious in the code, instead of hidden. To address (2), exceptions use type system, preventing you from compiling a program that has conflicting error "values". Morever, using an exception class hierarchy you can represent "families" of related results. (Note: this is often misused, and people catch Exception instead of NoMoreDiskSpace and still display the generic The operation failed. message).

A matter of consistancy

Some people will recommend a mixture of both in their applications, but IMHO, this leads to a situation where both the systems are mis-used. In these hybrid conventions, exceptions are often not caught and error-codes not checked because of confusion. On one hand, because exceptions are used only for exceptional situations, it is assumed they will never occur, or simply cannot be handled. On the other hand, a failure returning an error code is assumed to be minor and is not handled at all. And of course it is left up to each programmer to decide whether an situation is exceptional or not, leading to lots of confusion between what error codes to check and what exceptions to catch.

Which ever system you choose, be sure to use it at its full strength, and be consistent about its use.


You can look at exceptions and return values as two different methods of communication between callers and callee's. Return value being nice and quick way of informing parent about stuff, and exception being the rude cousin, one that you can fire and let others know how bad it is 'down there'.

If you write code that tries to handle every situation, you'll fill it with return values, error codes etc. If you omit handling some situation that occurs log way down on the call stack, and want quick resolution for this - someone will show MessageBox at this point - but you can throw, and handle it at any appropriate level above. Maybe that's the answer to your question!

On second thought, here are some rules that might apply:

  • use bool return values for simple success/fail situations
  • use enum return values for somewhat more complex situations
  • use ret. values for classes that know of each other, for example that serve same purpose or are in same module
  • use exceptions to throw far and account for unforeseen situations. Translate system errors that are not previously thought of to some exception of yours that you can handle on some higher level

I guess that both methods are here to stay. With time, your judgement will say which to use on each occasion.


There is a performance impact when exceptions are thrown that is the reason why error codes are used for more frequent error conditions. Save exceptions for truly out of the ordinary problems.

For example - in the STL, std::find does not throw if no match is located. But vector::push-back throws if memory is exhausted.


C++ exceptions are supposed to be used only for exceptional errors.

They are particularly useful for when an error is so bad that the calling function can't, or indeed shouldn't, handle it, e.g. a memory exhaustion. A thrown exception unwinds the stack until the first try block that it sees (or maybe the first try block with an appropriate catch, which would make more sense). This allows you to catch an exceptional error from a deeply nested function call, and be guaranteed that the stack is clean down to the function with the try block.

This all comes with overhead though, so use return codes when you can handle the (unexceptional) error inside the calling routine.


Along with the discussions already presented in the questions linked to by bjskishore123 in his comment, there is a very interesting historical back-and-forth between Ned Batchelder and Joel Spolsky on the subject. You may not entirely agree with Ned, but his argument is well thought out and worth a read.


Life is simple if your function has nothing to return - it can return a success/fail code. But when your function already has a meaning to its return value then you need to use "signal values" to return error codes. Find methods that return -1, for example, are using signal values. Sometimes it's almost impossible to come up with good signal values. If a function returns the next available date, what shoud it return when there is no available date? There's really no "-1" type date. So this is one case where exceptions are a very helpful mechanism. (This also handles functions like constructors that don't return anything.)

Yes, throwing and catching an exception is slightly more expensive than checking an error code. But it can't be forgotten and many people find it more expressive. And, as mentioned in other answers, when there is a cascade of function calls it's awkward to have all the intermediary calls check the return value and pass it along back up the chain.


Use error codes when it is not unusual for something to go wrong. For example, code that opens a file or makes a network connection should not return exceptions - it is very common for there to be problems.

Use exceptions when it is unusual for something to go wrong - the failure is truly exceptional.

So, that can be simplified to:

  • Use error codes most of the time.
0

精彩评论

暂无评论...
验证码 换一张
取 消