Exceptions vs. error codes

Suppose you have a function like this:
1
2
3
4
5
6
7
/*...*/ implementation_function(const std::vector<parameter> &params){
    //...
    check_parameters(params)
    //...
    do_it(params)
    //...
}

that implements some built-in function in a language.
How would you implement error handling for this function?
1. The function handles no errors. check_parameters() and do_it() throw exceptions caught in the main loop.
2. check_parameters() and do_it() return error codes which the function returns.
3. check_parameters throws exceptions caught in the main loop, but do_it() returns error codes.
4. Other.

Note: This is a highly abstracted example. Neither check_parameters() nor do_it() exist as such. There's a hundred other functions like implementation_function() and they all have their own unique checks and do's to perform, thus something like
1
2
if (failed(error = check_parameters(params)))
    return error;
doesn't work. It's more like
1
2
3
4
5
6
7
if (params.size() != k)
    return /*...*/;
if (params[0].type() != INTEGER)
    return /*...*/;
if (!param[0].is_variable())
    return /*...*/;
//... 
I come from a Java standpoint and would prefer exceptions as opposed to error codes. Both can be well documented, but exceptions are objects that can pass a message as opposed to just a number. Error codes pass the number and then can be sent to a lookup function to get more information. I think it just comes down to personal preference. The Windows API would be an example for error codes.
So to answer your question... I would choose 4. check_parameters() and do_it() throw exceptions, but the function solves the problem if it can otherwise it passes the problem up further by throwing an exception.
but the function solves the problem if it can otherwise it passes the problem up further by throwing an exception
This has all the disadvantages of exception handling plus all the disadvantages of error codes. If the exception is going to be caught as soon as possible, then it's pointless. You may as well simply make an Error class that contains extended error information and return that.
The point of using exceptions is that they can redirect control by unwinding the stack without any leaks, letting you write your code for the "happy path" and leave error handling for the compiler.
If your code throws an exception, there shouldn't be anything that can be fixed. The program state (or a subset thereof) is beyond all hope and the control flow needs to leave as soon as possible before it starts to smell.

Here are some "wrong exception-handling mindsets" in no apparent order:
* The Java mindset: In Java, non-memory resources are reclaimed via explicit try/finally blocks. When this mindset is used in C++, it results in a large number of unnecessary try blocks, which, compared with RAII, clutters the code and makes the logic harder to follow. Essentially the code swaps back and forth between the "good path" and the "bad path" (the latter meaning the path taken during an exception). With RAII, the code is mostly optimistic — it's all the "good path," and the cleanup code is buried in destructors of the resource-owning objects. This also helps reduce the cost of code reviews and unit-testing, since these "resource-owning objects" can be validated in isolation (with explicit try/catch blocks, each copy must be unit-tested and inspected individually; they cannot be handled as a group).
http://www.parashift.com/c++-faq/mindset-for-proper-use-of-eh.html

Catching exceptions in the implementation functions is simply not acceptable, or useful for that matter.
Was it too much to say if it can? For example a simple failed HTTP request could be retried? Or maybe an SMTP request can look for a backup MX server? Almost all exceptions should be sent to the caller, meaning no try catch should be in the implementation function if no easy fix can be applied. Personally I do not clutter my code with try blocks in C++, but my main function usually does a lot of try catch stuff. Most people, including me, would say to just get the heck out of dodge. I switched to C++ because I was tired of all the memory issues in Java. Java makes code less elegant and more cluttered with memory fixing code, not to mention a lot more try catch blocks than what really seems necessary. I still prefer exceptions because I am too lazy to deal with looking up error codes or writing a lookup function for custom error codes. I really do not see a reason for custom error code functions when I can just use the STL exception classes with specific messages.

There is not really a catch all answer to your question. I think it would really depend on the system and what it will be used for. All that being said, you did ask an opinion based question. My opinion is still choice 4.

And I am curious as to why you said...
plus all the disadvantages of error codes


I just read this
http://www.joelonsoftware.com/articles/Wrong.html

And I have to say there are some very good arguments against exceptions in this. I would change my opinion to choice 2, for systems that need to be reliable. I agree with the author on using exceptions for small projects.
Last edited on
I don't like exceptions in any languages...so, yeah, that's my stance. Error codes are also ugly, though... :(
For example a simple failed HTTP request could be retried? Or maybe an SMTP request can look for a backup MX server?
Those are good examples of code that could both throw and return error codes on different situations. It's not the same when a GET fails because of a transport error (e.g. the network is down) as when it reaches the server but it returns 404.

Now, imagine a function that opens a file in a dynamic language: open(). If "hello.txt" doesn't exist, is the error caused by open("hello.txt") of the same criticality as the error caused by open(42)? I think not, but from a point of view of pure design (this was my real question. Sorry about not making it clearer):

Which is better? A function that throws on all errors, or a function that throws on some errors and returns on other errors?

I think there's compelling arguments for both. Going back to the open() example, it's hard to tell whether open("hello.txt") actually failed. On the one hand, it tells the user whether "hello.txt" exists, so maybe it depends on the intent of the caller. On the other, the file is still not open after the return. Maybe it warrants a NotQuiteSuccessException, or FailureWithPostconditionsMetException?
It should be the constructor for or member function in a class, and you can check the state of the class afterwards.
Exceptions are simply a way to indicate failed preconditions (and invariants, but this thread is about preconditions). The other ways are termination and documented undefined behavior, which are both useful techniques too. Whether "file exists" is a precondition or not is up to the design - I've seen plenty of both (although "filename is a valid name for this OS" or "filename follows the naming pattern" makes a sensible candidate for std::logic_error, if it needs to be handled differently from "can't open")

Regarding the original question, I'd say exceptions from check_parameters and assert/UB from do_it, unless wrong parameters can be presented to this function within the logically valid workflow (in which case the function should return whatever that logic demands)
Regarding the original question, I'd say exceptions from check_parameters and assert/UB from do_it, unless wrong parameters can be presented to this function within the logically valid workflow (in which case the function should return whatever that logic demands)
Are you assuming that check_parameters() would, for example, check for the presence of a file, or that do_it() would?

I think I'm going to go with option #1, since it makes for the simplest code structure. I can simply throw in the deepest helper functions and maintain all error information, regardless of the semantic nature of the error.
closed account (zb0S216C)
helios wrote:
"How would you implement error handling for this function?"

That depends on what you do with the function. I would only throw an exception if certain circumstances arise that differ from normal program flow. In addition, exceptions should be used on seldom occasions due to its impact on performance and invisable exit points. Finally, throwing too many exceptions may make it harder for you to differentiate between a normal program flow error and a true exceptional circumstance.

Wazzak

@ helios: Is this a real world problem or just a thought experiment? Because although I'm not a programmer by trade I think the biggest issue here is that you are passing an array into this function for processing so no matter which solution you choose, an error in one object affects the processing of every other item in the array. Isn't that a design flaw? Why not make this function into a member of the class so that each instance of 'param' can handle individual issues as they come up? That way data validation is done in the constructor and errors in execution can be handled on a case by case basis. Depending on specifics this approach might also open the door for each object to be processed in parallel.
closed account (S6k9GNh0)
OpenGL does something good on this I think... it sets an error code for everything which I do not like (or rather, is kinda useless in realistic situations since you'd either have to wrap every GL function to check for an error or have to be very verbose by adding it to every block of code). They give you functions where you supply a callback for logging. When something goes wrong or something could be improved, an implementation defined message will be sent to the callback. It can be heavy though since OGL is used in real-time environments... but maybe it can be better applicated in non-real-time applications?
Last edited on
Computergeek01:
Is this a real world problem or just a thought experiment?
It's a real problem.

the biggest issue here is that you are passing an array into this function for processing so no matter which solution you choose, an error in one object affects the processing of every other item in the array.
Well, yeah. You normally don't want your function to run if one of its parameters is undefined due to run time errors.

Why not make this function into a member of the class so that each instance of 'param' can handle individual issues as they come up?
Which function? implementation_function()? That doesn't make a lot of sense. How would a parameter implement the function it's passed to? And about the other parameters? Does the function get called once per parameter?

That way data validation is done in the constructor and errors in execution can be handled on a case by case basis.
Ah, I see.
Parameters are constructed before the function that will use them is decided. Syntactical checks are of course performed immediately, but that's about it. Ultimately, the function is the only one who knows if something needs to be a string, or a variable, or whatever.
Now, technically it doesn't need to be like this. A table stating the number, types, and writability of parameters for each function could be assembled. But some functions are a bitch, really. Like the function that takes any even number of parameters. So eff that.

Depending on specifics this approach might also open the door for each object to be processed in parallel.
That would just be silly. Even with thread pools, the latency incurred is far too great. Checking a parameter costs one or two integer comparisons.

Framework: Since this function is part of a programming language (more specifically, part of an engine), no errors should occur during normal operation, only during script debugging. Assets shouldn't suddenly disappear, and a script writer shouldn't intentionally send wrong arguments to a function. So, an error would be a sign that something has gone very wrong -- such as data corruption in a user's machine -- or of logical or syntactical errors in the script. Theoretically, the number of errors as a function over time is bounded.
Topic archived. No new replies allowed.