Exception question

Started by
9 comments, last by SeanMiddleditch 9 years, 9 months ago

Hello Gamedev people, I'm new here.

I have a question related to C++ std::exception.

Let's take this example:

try {

int* ptr = new [10];

for (int i = 0; i < 10; i++) ptr = 10;

throw 1;

} catch(int x)

{

}

Does the allocated pointer is automatically deleted when exception is thrown?

Or, if not, how to handle it?

Basically I'm writing a game framework and as error-handling I'm using exceptions.

Advertisement

Pointers never deallocate themselves. Use a smart pointer object to manage ownership.

try {
    std::unique_ptr<int[]> ptr(new [10]);
 
    for (int i = 0; i < 10; i++) ptr[i] = 10;
 
    throw 1;
} catch(int x)
{
}
No, the allocated pointer is not freed. The same would go if that where a function call or any other kind of block, the memory would be lost.

With C++11 there is std::unique_ptr or std::shared_ptr. Without C++11, I would advise using the pre-C++11-implementations in Boost or tr1. I would always advise against the deprecated std::auto_ptr (especially because it would call the wrong delete in this case).

Ok thanks guys

For future reference if you're going to use exceptions in C++, you should throw by value and catch by reference. I know that isn't going to make a lot of difference if you're throwing some POD type like an int (which, by the way, is not a std::exception), but it will matter when you're throwing more complex types or trying to handle standard exceptions from the STL or other libraries.

Pointers never deallocate themselves. Use a smart pointer object to manage ownership.

try {
std::unique_ptr<int[]> ptr(new [10]);


This code is still a dangerous pattern, though it's not an issue in this exact case. Never ever use `new` except for placement-new inside of data structures that need it. Use std::make_unique instead.

See http://herbsutter.com/gotw/_102/.

Or of course just disable exceptions and avoid the entire rat's nest of exception-safety problems. (But still don't use `new` or `delete` directly; they're a bad memory management interface.)

Basically I'm writing a game framework and as error-handling I'm using exceptions.


Save yourself the pain and don't. Read http://bitsquid.blogspot.com/2012/01/sensible-error-handling-part-1.html (and the other articles in the series).

Sean Middleditch – Game Systems Engineer – Join my team!

Pointers never deallocate themselves. Use a smart pointer object to manage ownership.

try { std::unique_ptr<int[]> ptr(new [10]);This code is still a dangerous pattern, though it's not an issue in this exact case. Never ever use `new` except for placement-new inside of data structures that need it. Use std::make_unique instead.See http://herbsutter.com/gotw/_102/.Or of course just disable exceptions and avoid the entire rat's nest of exception-safety problems. (But still don't use `new` or `delete` directly; they're a bad memory management interface.) Basically I'm writing a game framework and as error-handling I'm using exceptions.

Save yourself the pain and don't. Read http://bitsquid.blogspot.com/2012/01/sensible-error-handling-part-1.html (and the other articles in the series).


I would respectfully disagree. Exceptions tend to make code faster (if you're using them for error states and not flow control), safer, and easier to read. Their only downside that I'm aware of is they do increase code size due to the dispatch tables. (And sure, they can be slow when you throw them, but you shouldn't be throwing them all the time - they're exceptional)

If you aren't using exceptions, you're usually writing your error handling code as if it were exceptions, except by hand, so more prone to error, more jumps, etc. Or you're not doing any error handling at all or being inconsistent which is probably worse.

Writing exception-safe code is just as hard/easy as writing error safe code, no matter your error mechanism. You just have to decide on it early and know and utilize the basic/strong/nothrow guarantee model. You are correct in that it is hard to write code to enforce the strong or nothrow guarantees, but most code only needs the basic guarantee, and if you're using proper RAII for all resources (not just memory) you've almost always got the basic guarantee with no extra work.

If your code isn't exception-safe, then it probably isn't error-safe either. Even if you're using return values, error codes, or whatever.

There is a good presentation recorded from BoostCon 2012 if you wish to know more.
I disagree with your disagreement. smile.png

I would respectfully disagree. Exceptions tend to make code faster (if you're using them for error states and not flow control), safer, and easier to read.

Writing exception-safe code is just as hard/easy as writing error safe code, no matter your error mechanism.

If your code isn't exception-safe, then it probably isn't error-safe either. Even if you're using return values, error codes, or whatever.


The assumption that "exception safe" is the same thing as "error safe" is wrong. Exceptions are a _mechanism_ of dealing with errors, not errors themselves. Exception-safe code has to deal with safety from that particular mechanism as in addition to being safe against the errors themselves.

There are many ways of making sure coders are dealing with errors when necessary. The world is not all exceptions, return codes, and errno.

Exceptions are a failed experiment and the CS community has already proven that you can do much better be it in realms needing extremely safe error-handling (medical devices, aviation, etc.) or realms needing easy error handling (games) and everything in between.

Sean Middleditch – Game Systems Engineer – Join my team!

Exceptions tend to make code faster (if you're using them for error states and not flow control), safer, and easier to read. Their only downside that I'm aware of is they do increase code size due to the dispatch tables

Increasing code-size, with all the extra potential unwinding cases added everywhere, can have a huge performance impact. Even just having exception support enabled in your compiler (regardless of whether you even use them or not) makes your code slower sad.png

Libraries targetted at games generally are forced to avoid using exceptions at all, because many game-developers disable them for performance reasons (the compilers for consoles have them disabled by default, with a compiler option to enable them if they're really required!).

n.b. my statements are only true for C++'s exception system -- the mechnisms in newer languages are much nicer... Also C++'s mechanism is much nicer on x86-64 than it is on x86/PPC/ARM/etc...

Writing exception-safe code is just as hard/easy as writing error safe code, no matter your error mechanism.
... You are correct in that it is hard to write code to enforce the strong or nothrow guarantees.

Yeah - strong exception safety is very hard. You've got to be constantly mindful that any line in your program could potentially be a hidden return statement, and make sure that the program will always be in a valid state at all times (except when you're completely sure that the lines in question give you the nothrow guarantee).

When exceptions are disabled (or you give up on the strong guarantee), that massive mental tax goes with them wink.png I personally hate the exceptions-mental-tax, because most IDE's suck at informing you whether a line of code can potentially throw or not, so it's not easy at a glance to tell how safe some bit of code is. If throw-specifiers / noexcept weren't broken features, this tax might be lifted somewhat...

If you're using return values to report errors, the alternative mental tax is that you've got to be constantly mindful that functions have return values that you may have to check...

Usually in games, "expected errors" are extremely rare anyway, so the debate over the correct style only concerns a very small amount of code. If you expect an error to occur though (e.g. you tried to insert a new key-value into a map, when that key was already present) then most people will say that exceptions are the wrong mechanism.

"Unexpected errors" in games are usually just given straight to the crash-handler, with no need to write recovery code (just a need to generate good debugging information to fix the code/data).

I disagree with your disagreement. smile.png


What? Civilized disagreement? I thought this was the internet...

The assumption that "exception safe" is the same thing as "error safe" is wrong. Exceptions are a _mechanism_ of dealing with errors, not errors themselves. Exception-safe code has to deal with safety from that particular mechanism as in addition to being safe against the errors themselves.

There are many ways of making sure coders are dealing with errors when necessary. The world is not all exceptions, return codes, and errno.

Exceptions are a failed experiment and the CS community has already proven that you can do much better be it in realms needing extremely safe error-handling (medical devices, aviation, etc.) or realms needing easy error handling (games) and everything in between.


I'd be interested in seeing some of these mechanisms because I'm used to using exceptions in my hobby/tool code, and dealing with return codes/error codes at work. If you aren't using an exception, then you're propagating an error code yourself (or ignoring it). This can make it harder for the compiler to work with the branch predictor as it cannot necessarily determine which branch is the failure state. Admittedly, if you're doing it yourself there is less "hidden" code, but the same can be said about RAII and virtual dispatch and, for the most part, we've accepted the compiler can do those better then we can. (And/or we accept the size/performance tradeoff)

Any elaboration on a real error-handling sliver bullet would be great smile.png

Exceptions tend to make code faster (if you're using them for error states and not flow control), safer, and easier to read. Their only downside that I'm aware of is they do increase code size due to the dispatch tables

Increasing code-size, with all the extra potential unwinding cases added everywhere, can have a huge performance impact. Even just having exception support enabled in your compiler (regardless of whether you even use them or not) makes your code slower sad.png
Libraries targetted at games generally are forced to avoid using exceptions at all, because many game-developers disable them for performance reasons (the compilers for consoles have them disabled by default, with a compiler option to enable them if they're really required!).
n.b. my statements are only true for C++'s exception system -- the mechnisms in newer languages are much nicer... Also C++'s mechanism is much nicer on x86-64 than it is on x86/PPC/ARM/etc...


True, you can potentially run into more cache misses and similar problems with the increased code size, but you'd have to measure that. (I don't have an exception-safe code base to test against an equivalent non-exception-safe code base in a meaningful manner at this point) However if you're using error codes/return values you're either ignoring the errors (questionable practice, but hey, we're games, not medical software) or you're adding a lot of ifs/branches that the compiler can't necessarily predict, which may be worse (again, would need measurement in your use case).

Writing exception-safe code is just as hard/easy as writing error safe code, no matter your error mechanism.
... You are correct in that it is hard to write code to enforce the strong or nothrow guarantees.

Yeah - strong exception safety is very hard. You've got to be constantly mindful that any line in your program could potentially be a hidden return statement, and make sure that the program will always be in a valid state at all times (except when you're completely sure that the lines in question give you the nothrow guarantee).
When exceptions are disabled (or you give up on the strong guarantee), that massive mental tax goes with them wink.png I personally hate the exceptions-mental-tax, because most IDE's suck at informing you whether a line of code can potentially throw or not, so it's not easy at a glance to tell how safe some bit of code is. If throw-specifiers / noexcept weren't broken features, this tax might be lifted somewhat...
If you're using return values to report errors, the alternative mental tax is that you've got to be constantly mindful that functions have return values that you may have to check...

Usually in games, "expected errors" are extremely rare anyway, so the debate over the correct style only concerns a very small amount of code. If you expect an error to occur though (e.g. you tried to insert a new key-value into a map, when that key was already present) then most people will say that exceptions are the wrong mechanism.
"Unexpected errors" in games are usually just given straight to the crash-handler, with no need to write recovery code (just a need to generate good debugging information to fix the code/data).


I think a large part of it comes from proper SRP use. So if your resources are held by objects solely concerned with holding them, it makes your job a lot easier. I think you're going to have a large mental tax either way. Either you know that an error will hop out of your code, or you manually code in the return value checks and hop out of your code yourself.

The largest mental tax, IMHO, is determining what constitutes an exception vs. what doesn't. But that's something that you have to handle no matter your mechanism. Does this error mean a code flaw? Then assert or crash. Does it mean a data flaw? Maybe then you want to use a placeholder instead or throw a warning to the user. Part of that choice is going to come down to who most likely generated the bug (coder or designer/artist) and who is most likely able to fix it. Exceptions can help because they are great at getting errors from where they happen to code that can actually make decisions about what to do about it (crash, open a dialog box, replace with placeholder, etc).

In my opinion, these are the largest reasons why they are not used in AAA development today:
  • Old compilers didn't do exceptions well. This means older code bases are going to be full of code that didn't use them, and don't expect them.
  • Older hardware (and mobile hardware today to some extent) didn't have the memory to spare for a larger executable size that exceptions and RTTI required, so they were disabled. This, again, results in a code base that doesn't expect them.
  • Exceptions are not language-neutral. Your libraries with C interfaces cannot throw exceptions, and you can't let exceptions escape your code into other code that uses a different language.
  • Exceptions cannot escape threads - requiring you to come up with some sort of home-grown cross-thread propagation mechanism.
Nothing insurmountable, but it's much easier for someone to use exceptions in a hobbiest/indie situation where they aren't dealing with large amounts of non-exception-safe (and heck, frequently non-RAII) code and where their game may not need to squeeze out every ounce of power out of the machine via ignoring errors (or crashing).

This topic is closed to new replies.

Advertisement