Exceptions

Pages: 123
Personally I dislike exceptions. It seems other people do as well:
http://stackoverflow.com/questions/1736146/why-is-exception-handling-bad
http://blogs.atlassian.com/2011/05/exceptions_are_bad/
etc... etc...

But the alternatives aren't any better, and in fact they only use a different syntax for the same thing, maybe with a slight 'benefit' that they don't have to have the same restrictions that the language designers intended. This doesn't seem better at all to me.

It seems like people can't decide how to handle it when things go wrong. There's your problem - things going wrong! Why is code designed in such a way that it can go wrong? It isn't.

The only time code tends to go wrong is with bad user-input. So why even accept bad user input in the first place? What I commonly see exceptions being used for is catching a user-input error 100 stack levels down from where the user input was originally accepted.

How does ParseNumberFromString handle something that isn't a valid number in string format? It doesn't, because that's not its responsibility - its responsibility is to do the best it can at parsing a number from that string. If it can't it can return a default value provided to it or just return 0. The calling code should be is responsible for making sure everything is valid. Otherwise, you're breaking encapsulation.

By handling invalid user input as soon as possible, you decide exactly how your code reacts - if it fails, if it informs the user, if it continues silently in a corrupt state - you decide based on your needs and the application.

If you take invalid input, don't leave it to a function lower down or higher up in the call stack to deal with it - handle it, fix it, or ignore it right then and there. I've certainly never seen a person rely on the car or its manufacturer to handle the case when they start their car with no gas. It's invalid user-input, so the results are implementation-defined.

What happens when something is implementation defined? People don't do it :) So not handling an error state that is your responsibility should be considered as having the same taboo as being implementation defined.

So what if you ask a library to parse a JSON for you, and that JSON is invalid? You can't tell because only the library knows what is valid or not. Many would say that the client code should be held responsible, since it gave the library that bad user input, so therefore the library should call back and ask the calling code what to do. But doesn't this sound familiar? Stopping and going up the stack to see if the calling code can deal with it? It is just a different kind of exception handling.

But let's think here - if the JSON is invalid, we probably don't want to try using its data, so this case shouldn't even be handled. So you would receive false from parse() and handle there, right? This sounds familiar - stopping and going up the stack to let the calling code deal with it. Yes, we've just found yet another way to do exception handling.

So what can you do? Well, either you have to deal with the poorly designed library, or you can fork it since it's open source and fix the problem. Have it take a default state, e.g. a default json to parse that is guaranteed to be valid or else the result will be empty. Have a fallback - never deal with failure. If you need to know that it's the default state, include that as part of the default.

This is what you would do in real life.
You wouldn't let your car deal with you not putting gas in it.
You wouldn't let the dealership deal with you not putting gas in your car.
You wouldn't deal with your kid not putting gas in their car.
You wouldn't ask your parent to deal with you not putting gas in your car.
Either you put gas in your car, or you walk to work. You have something to fall back on - don't deal with it, just use an alternative guaranteed to work.

Some people complain about having to have default values everywhere, or about having to validate user input. I think this is ludicrous to complain about it - you only have to do it once in your code and never again, and it properly encapsulates everything.

Solution: Either guarantee you're operating with valid data, or provide default valid values to use, possibly including the fact that they are default values.

Please argue any points you find may be incorrect, you don't understand, or you don't agree with - I'm always open to new ideas and can admit that I am wrong. Also note that I actively came up with the answers and solutions to my questions and problems as I wrote this, so I may have contradicted some of my previous statements.
The only time code tends to go wrong is with bad user-input.
Also missing files, suddenly severed network connections, IO system errors, overloaded CPU which cannot provide minimum required computation speed, hardware failures, and who knows what else.

How does ParseNumberFromString handle something that isn't a valid number in string format
C++14 Optional class is perfect solution for this: such function should tell that something is wrong, it shouldn't silently hide errors and substitute them with some arbitrary value. There is no sense to have two functions like isNumber and toNumber: if you have two functions which should be called consequently to avoid errors, you should combine them in one function call.

By handling invalid user input as soon as possible, you decide exactly how your code reacts
I agree. That usually means that you should transform input in some machine-readable format immideatly after aquiring it without storing them in temporary buffers to handle later.

You wouldn't let your car deal with you not putting gas in it.
It trows out of gas exception and lets you handle it. If you don't and it happens on the road stack unwinds further and state tows you car away and you have to pay money to return it :).

Problem with exceptions is many people misuses them. In my opinion exception indicates that something horribly happens and handling code should restore program to previous state.
For exaple I worked on some system which have used transactions to send and receive data. If there is a (network) failure during an transaction (one of several possible exceptions) it saves info about failure, saves some data on transaction, notifies user about failure and gives him a choice to enqueue transaction to be sent again as soon as network will be up or discard it. If he chooses to it enqueues that transaction in ResendQueue. Also it enqueues error info in ErrorQueue to be sent to server and saves error data in local log. Either way it return program state like it was before transaction allowing user to continue work.
Last edited on
Sometimes there's no enough information to know in advance whether the input is valid or not. One time I was working on a language with an Assembly-esque syntax. Some commands had a variable number of arguments of variable types. I ended up with this:
1
2
3
4
5
6
7
8
9
10
11
12
13
void some_command(Parameters &param){
    CHECK_PARAM_COUNT(3);
    std::wstring foo, bar;
    int baz;
    float banana;
    GET_INTEGER(baz, 2);
    if (baz % 2)
        GET_STRING(foo, 0);
    else
        GET_FLOAT(banana, 0);
    GET_STRING(bar, 1);
    //do stuff...
}
Those are macros that check the return values from other functions and cause a return in case of errors. Note that: a) the required type depends on a run time value, and b) the type of the first parameter may also be dynamic.
In retrospect, the overhead saved by not using exceptions was not worth the ugliness of this kludge.
MiiNiPaa wrote:
Also missing files, suddenly severed network connections, IO system errors, overloaded CPU which cannot provide minimum required computation speed, hardware failures, and who knows what else.
Which should not be handled because they are out of your control.

MiiNiPaa wrote:
C++14 Optional class is perfect solution for this: such function should tell that something is wrong, it shouldn't silently hide errors and substitute them with some arbitrary value. There is no sense to have two functions like isNumber and toNumber: if you have two functions which should be called consequently to avoid errors, you should combine them in one function call.
So we're going back to checking return types like years ago?

MiiNiPaa wrote:
It trows out of gas exception and lets you handle it. If you don't and it happens on the road stack unwinds further and state tows you car away and you have to pay money to return it :).
Your car also isn't responsible for making sure you have enough gas in it to go where you want to go.

MiiNiPaa wrote:
Problem with exceptions is many people misuses them. In my opinion exception indicates that something horribly happens and handling code should restore program to previous state.
For exaple I worked on some system which have used transactions to send and receive data. If there is a (network) failure during an transaction (one of several possible exceptions) it saves info about failure, saves some data on transaction, notifies user about failure and gives him a choice to enqueue transaction to be sent again as soon as network will be up or discard it. If he chooses to it enqueues that transaction in ResendQueue. Also it enqueues error info in ErrorQueue to be sent to server and saves error data in local log. Either way it return program state like it was before transaction allowing user to continue work.
I agree, but I don't see how this is exception handling. It's more like going back in time to when you had the chance to put gas in your car, which is a good idea.

helios wrote:
Sometimes there's no enough information to know in advance whether the input is valid or not.
I think I discussed this in my first post. In your example I don't see why you need to be responsible for handling incorrect usage of your properly documented function - could you elaborate?
Last edited on
I don't see why you need to be responsible for handling incorrect usage of your properly documented function


Because the users will choose the competitor's software which handles misuse, doesn't let the hackers in when they type shellcodes in standard input, and doesn't irreparably jam the car engine when a sensor dies of old age.

"Garbage in - garbage out" was a popular programming idiom in the 70s, but it didn't survive the competition.

(that said, undefined behavior when a narrow contract is violated is a valid programming approach in some cases, as long as it is properly documented -- see vector::operator[])
Which should not be handled because they are out of your control.


Actually these should be handled by your application because you must guarantee integrity of data in the event of failure. This is especially important when working on critical systems (e.g healthcare, science, money).

It's not acceptable to say to your client "oh yea sorry those patient records are gone now because of a network failure". Welcome to the unemployment line with a possible lawsuit following.

Edit: Typos =\
Last edited on
In your example I don't see why you need to be responsible for handling incorrect usage of your properly documented function - could you elaborate?
Imagine a C++ compiler (or any compiler for that matter) that aborted with no output when handed a program with syntax errors. Not terribly useful. Even worse, imagine a compiler that when given that same program silently produced incorrect code.

A program is ran more often than it's written, so GIGO is a false economy.
@Zaita: Please reread the second-to-last response in this post:
http://www.cplusplus.com/forum/lounge/101993/#msg548368
Which should not be handled because they are out of your control.
A natural disaster is out of your control too but this doesn't mean that you should go with your business when one is happening in your backyard.
My first major failure was when I forgot to handle networ failure situation and results of three day work was sent to nothingness and then system reset itself erasing that data from local storage. Funny thing that code handling such situation already existed, it just wasn't executed because I forgot to compile networking module with exceptions on (#undef NO_EXCEPTION )

So we're going back to checking return types like years ago?
If you are handling input which might be not valid it is your responsibility to check that everything all right. It doesn't matter how you would do this. In C++14 thread I suggested that optional can be implicitly converted to its value type, default initialized in case if it doesn't holding value:
1
2
3
4
5
6
7
8
optional<int> o;
int x = o;// = 0 because o doesn't hold value
o = 5;//imlicitly assign 5 to value and set 'holding' flag
x = o;// = 5, o holds value now
if (o) //implicit bool conversion
    //manipulate data in o
else
    //do something if there isn't anything in o 
That way you can check if operation is successful or you can do not do this and get default value. Also called code could pass responsibility for input to calling code.

Your car also isn't responsible for making sure you have enough gas in it to go where you want to go.
It is responsible to make sure it cannot be run without gas. For now it implemented using physics laws, but these are implementation details which you shouldn't rely on. Also your body will send you signals (exceptions) when it is hungry. You could deal with these signals however you want. If it just silently continued to work, you might not notice that you have unsufficient nutrients until you look like concentration camp prisoner.

I agree, but I don't see how this is exception handling. It's more like going back in time to when you had the chance to put gas in your car, which is a good idea.
It is like Emergency Services work when crowded hotel throws gas line exploded exception: make sure that all people evacuated and kept alive, that hotel sustains as little damage as possible and if worst happens that nearest buildings won't de damaged. Then hotel owner will fix damage caused by explosion and fire and voila, exception handled and hotel is ready to accomodate new guests.
The whole point of exception that if unhandled exception will stop your program execution in case it can go haywire in result of error and lose your data. If handled it sould isolate problem and make sure that program is in working state and avoid data (or work) loss. Because in most cases there always something that should not dissapear suddenly because of some error.

I think I discussed this in my first post. In your example I don't see why you need to be responsible for handling incorrect usage of your properly documented function - could you elaborate?
You should not handle this. That is why you throw an exception. Or retunt optional without value. Or send another signal.
How calling code can handle situation when input data becomes invalid in middle of execution? For example you call function which dumps all data about your system in some file. You have checked that this file exist, you have checked that you have permission to write in it, everything. You call that function. In a middle of writing antivirus blocks file, it got deleted or data was larger than usual and we run out of disc space. Should you function pretend that nothing happens or it should signal "there is something I cannot handle so I leave rensponsibility to you"?
Last edited on
Off-topic:
1
2
3
4
5
6
7
8
9
#include <windows.h>
#include <limits.h>

int main()
{
    HDC dc = CreateCompatibleDC (NULL);
    SetLayout (dc, LAYOUT_RTL);
    ScaleWindowExtEx (dc, INT_MIN, -1, 1, 1, NULL);
}
WARNING: will almost surely cause a bluescreen on Win 7/8

Now say again that you shouldn't be responsible for input checking.
(not my code, just saw an article about it)
Last edited on
Exceptions are better than custom error codes or Optional type because you can't accidentally forget about error checking and let your program run in corrupted state. And you also don't need to check every method call for errors, like in C, where just almost every statement is in a form of:

1
2
if (dosomething() != 0)
   goto error;


which is actually a poor-man exception implementation.

Most of the critics to the exceptions is targeted mostly at:

1. Particular C++ exceptions implementation (don't play well with manual memory management and there is no finally clause to assure clean exit - and RAII isn't a silver bullet either)

2. Checked exceptions (because they are not transparent and you cannot just signal a coletely new error condition in a lower-tier of the app and catch it in the upper-tier without being forced to modify all the methods in the mid-tier). Checked exceptions play particularly bad with lambdas and anonymous classes.
Last edited on
MiiNiPaa wrote:
Also your body will send you signals (exceptions) when it is hungry. You could deal with these signals however you want. If it just silently continued to work, you might not notice that you have unsufficient nutrients until you look like concentration camp prisoner.
Now you're telling me that sending messages is the same as exceptions?
Ok, Your body starts to pass out because of hunger periodically. Now that is an exception.
Sometimes when I drink too much I end up vomiting uncontrollably and can no longer stand very well.
I don't recall bashing message sending...
I do not recall that too. It looks like my analogy was incorrect so I just fixed it. We talking about exceptions, not messages. It is to early to derail thread: we don't even started second page!
Your passing-out routine wouldn't throw an exception and go up the call stack to your brain, stopping everything along the way - it'd just send a message. This is what I was trying to say - that there are much better alternatives to exceptions.

Which post did you edit? I can't tell.
Meh. I am bad at analogies...
Messages should be send if something should be paid attention for or dealt with later. Exceptions should be throwed when shit hits the fan and you have to minimize damage. Disregard my human analogy. He is nothrow apparently. Look at my hotel one instead :)
And it does not send message. It stops working completely and let outside world handle it. Then it could be started again. Or not, if it damaged beyound reapairs.

Which post did you edit? I can't tell.
Oh, I replied to your post pointing that it looks more like message with another (bad) analogy.
Last edited on
Ah, OK.
MiiNiPaa wrote:
It is like Emergency Services work when crowded hotel throws gas line exploded exception: make sure that all people evacuated and kept alive, that hotel sustains as little damage as possible and if worst happens that nearest buildings won't de damaged. Then hotel owner will fix damage caused by explosion and fire and voila, exception handled and hotel is ready to accomodate new guests.
The whole point of exception that if unhandled exception will stop your program execution in case it can go haywire in result of error and lose your data. If handled it sould isolate problem and make sure that program is in working state and avoid data (or work) loss. Because in most cases there always something that should not dissapear suddenly because of some error.
The way I see it, the hotel (program) is part of the city (OS) and the gas explosion (out-of-memory) is an issue with the OS or one of its libraries. The program should exit (people evacuate from the hotel) saving its data to disk (people getting to safe places), and let the OS/user (city) deal with the problem. I don't think the program should handle running out of memory besides exiting and letting RAII save data as the stack unwinds.
Nope there is a OS (Earth) running several large business programms (Cities). The hotel is one of many subsystems of said program. And it should make sure that it will continue working because cost of hour of idling is larger than my yearly payment.
I don't think the program should handle running out of memory besides exiting and letting RAII save data as the stack unwinds.
I think that too. But how should program handle sudden HDD failure in a middle of writing several days works result? And should it?

BTW. Does somebody run my off-topic program?
Last edited on
Pages: 123