Portable Use And Disabling Of Exceptions

Started by
30 comments, last by Bacterius 11 years, 1 month ago

In the second case, if creating an OpenGL 4 context with all desired extensions fails, same is tried for an GL3 context, and if that one also throws, exception will make its way up to main where it's caught, an error is logged, and abort is called.

There is no advantage here over just abort()'ing directly after failing to create the GL3 context. The OS is going to deal with freeing all your resources when you abort(), so why bother unwinding all the way to main?

In addition, you are handling the error right when it occurs - you might just as well inspect the return value.

Enter exceptions. An exception handler could replace the missing texture with a "fail texture" (e.g. red/white checkerboard) and output a message on the console.

And so could the code that reads a return value. There is nothing special about exceptions here - the magic is all in the handler. And woe betide the programmer who forgets to write said handler (or doesn't know he needs to)...

And if you do, you haven't understood the "exceptional" part of "exception".

This is perhaps my biggest pet-peeve about exceptions:

  • Did you write code that checks a condition and then throws an exception on failure? This isn't an exceptional situation, it's an anticipated and error-checked situation.
  • Did you write an exception handler? This isn't an exceptional situation either, it's an anticipated and handled error situation.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Advertisement

There is no advantage here over just abort()'ing directly after failing to create the GL3 context. The OS is going to deal with freeing all your resources when you abort(), so why bother unwinding all the way to main?


There's nothing wrong with doing a clean exit, and there is nothing wrong with code that is clear and concise. The mere fact that the OS will close your handles and such isn't really an excuse. If it was, you'd just do a *(void*)0; when the user selects "quit" from the menu. That will certainly quit the program, too :-)

But more importantly, you're (deliberately, as I must assume) neglecting that printing out a one-line-message followed by abort is not what exceptions are meant for (even though this is what most people do anyway, almost all the time).

Aborting the process is the absolutely last thing you do, if there's no other options. If rudely killing the process when "something bad" happens is your intent, you can indeed just as well call abort. You'll have 8 keys less to type for the same effect.

Exceptions are for cases where you might have a chance of recovering in some way, or where you might at least have a chance of crashing less hard.

Such as trying another thing if the first (which is expected to work most of the time) fails. Or, for example, a program that has been writing to disk might want to exit cleanly even in the presence of a problem that prevents it from continuing -- instead of killing the process and forfeiting the data in the buffers. Or, it might want to delete all open (writeable) files under the assumption that they are possibly incomplete/unusable, and such files shouldn't stay around. The handler doesn't even need to know what went wrong or where, it doesn't care in this case.

This could of course also be done without exceptions, but not as cleanly, in one place, with a single handler for a hundred possible reasons.

woe betide the programmer who forgets to write said handler (or doesn't know he needs to)...


True, but... the same is true for return codes, except for one tiny detail. If someone doesn't check return codes, it "works" and doesn't work, and nobody can tell why. Or, it works for a while and then crashes at a completely unrelated occasion because some pointer is NULL. Or, it simply corrupts data and you never find out until 2 months after shipping when your boss starts yelling at you. Or, something different.

If someone forgets to handle exceptions, the application will be terminated with a human-readable message that is (hopefully) somewhat meaninful, at least to the guy who wrote the code. It will not go unnoticed.

This isn't an exceptional situation, it's an anticipated and error-checked situation. This isn't an exceptional situation either, it's an anticipated and handled error situation.


Both are true, and both are silly arguments against exceptions. Not the code that you write is exceptional, but the occurrance of an exception, that is, the condition failing.

Exceptions assume that your code and the data is "generally OK" and it "generally works", most of the time, and they are a way of still ensuring that something is done in the exceptional case when that's not true (and, without making the code unreadable, though what's "unreadable" and what is not is probably a very personal matter of taste, you might not agree on that part).

It would be really nice if I magically didn't have to think about errors ever again, by flipping the "compile with exceptions" switch. But of course, you still have to write code that anticipates (and handles) failure.

True, but... the same is true for return codes, except for one tiny detail. If someone doesn't check return codes, it "works" and doesn't work, and nobody can tell why. Or, it works for a while and then crashes at a completely unrelated occasion because some pointer is NULL. Or, it simply corrupts data and you never find out until 2 months after shipping when your boss starts yelling at you. Or, something different.

Alexandrescu published Mandatory Error Codes way back in 2005, and C++11 move semantics remove the remaining ick factor from his implementation. I'm not sure why this managed to largely fly under the radar for the last few years, but it solves the problem very elegantly - just don't allow your clients to accidentally ignore error codes.

(incidentally, he included exception-safe constructors in the same article)

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

But more importantly, you're (deliberately, as I must assume) neglecting that printing out a one-line-message followed by abort is not what exceptions are meant for (even though this is what most people do anyway, almost all the time).


The reason this is often done is because it is cleaner, and much harder to get wrong, than checking error codes after each goddamn function call and selectively figure out what needs to be done upon failure (e.g. send an error code through this and that socket, write something to the log file, delete such and such file, etc..). Bubbling the exception up the call stack provides an elegant and intuitive way of handling this, regardless of whether exceptions were meant for this or not. Many non-trivial applications cannot just dump core and require a complicated, data-dependent approach to failure (for recovery, cleanup, feedback, etc..) which would lead to a lot of duplicate code using error codes alone.

If you want to find out what a modern language using only return codes looks like, check out Golang. The error handling is an absolute mess and makes code incredibly frustrating to read as each function call is accompanied by two or more boilerplate error-checking lines. Just because it works doesn't mean it's perfect, otherwise we would still be using 4-bit assembly "because it works". Though it is a matter of taste, to some extent.

As for error recovery, well, it depends on what your program is and how it is to be used. If it's a command-line tool, you probably don't want to waste time trying to guess the user's thoughts and attempt to recover every time. No, you just print out some error, crash, and let the user fix it. On the other hand, if you are writing a GUI utility program, if there's an error you want a dialog box to pop up saying that something went wrong, to let the user try again, rather than crashing to the desktop and letting the user guess where the log file is.

Of course, if you run out of memory, or some other general runtime failure, there is not much you can do. Doing anything nontrivial in your error handler will probably cause the program to fail again, sending it crashing and burning, which leads to all sorts of interesting effects, such as, say, deleting your entire home directory wink.png

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

This topic is closed to new replies.

Advertisement