Jump to content
  • Advertisement
Sign in to follow this  
SunTzu

Exceptions, and the stack

This topic is 4723 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've been programming in C for a long time now (not necessarily through choice, it's what we use at work) but have frequently had to dabble with C++, often when interfacing with other peoples' or library code. But, I'm aware my understanding of C++ is not as good as it could be, and with some big deadlines out of the way I figure now's as good a time as any to fix that. Today's problem :-) is exceptions. So, I understand the concept of using them, and can write syntactically correct code to use them (though I still think it's horrifically ugly, I don't like the code that using them produces). My problem is, I just don't get what they're doing on the stack. Calling functions is easy - the code just pushes and pops various different variables and program counters onto and off the stack, no worries, easy as you like. But I don't see how exceptions could work the same way (how would you know the difference between returning from a function and throwing an exception from it?) and it confuses me. Could somebody please take a few moments to briefly explain what happens at a low level when an exception is thrown (and caught)? I'd much appreciate it!

Share this post


Link to post
Share on other sites
Advertisement
When an exception is thrown, the executable goes into meltdown mode. A copy of the exception is copied into a safe area, and then the stack is unwound. The destructors of all objects located on the stack are called in the reverse order of their construction until a catch block that handles that exception is found. Once the catch block is found, program execution resumes at the beginning of the catch block.

Share this post


Link to post
Share on other sites
Why do you think that the code is ugly?

One thing of which to beware is that you aren't using them like error codes, where every function call is wrapped in a try block with its own catches.

Also, be sure that you're using RAII ( which is a good idea whether or not you're using exceptions ).
// the ugly non-RAII version
void g() {
int *p;
try {
p = new int;
f(p); // might throw an exception
std::cout << *p << std::endl;
} catch (...) {
delete p;
throw; // rethrow the exception
}
delete p;
}

// the RAII version
void g() {
std::auto_ptr<int> p( new int );
f( p.get() ); // might throw
std::cout << *p << std::endl;
// auto_ptr's destructor deletes its pointer automatically
// when it goes out of scope, be it because of an exception
// or because the function terminates
}



On the same note, std::vector takes care of new[]/delete[], std::fstream will automatically close its files, etc. Guru of the Week has lots of good articles about excetion safety and RAII and such, for more information.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
A copy of the exception is copied into a safe area, and then the stack is unwound.


Could you elaborate on this "safe area" a little bit? Is it just some reserved space in the executable's data segment, or something more complex?

Share this post


Link to post
Share on other sites
It's implementation defined. It could be in the data segment of the executable or it could be dynamically allocated at the beginning of program execution. About the only restriction is that it can't be on the stack since that would cause it to be destroyed when the stack unwinds. But that's all it really is: somewhere not on the stack that you can safely stick the exception object until it's handled by something. This is why doing things like declaring an exception on the stack and throwing a pointer to the exception object isn't safe, since only the pointer will stored in the safe area, and the exception object would be destroyed during stack unwind.

Share this post


Link to post
Share on other sites
Thanks SiCrane and Me22, much appreciated. It makes a lot more sense to me now.

Me22, the code appears ugly basically because (in my limited experience) there's more typing involved, it's harder to format in a way that's easier to read, and it becomes MUCH harder to follow a path of execution. The number of exit points from a function increases and since (as you point out) the calling function isn't always the one that has to deal with the error, it means you have to go searching through many apparently unrelated code files to find out where (or indeed if) the exception gets caught.

I'm hesitant to say "I wouldn't use exceptions" for that reason because they do have uses but there was a scientist who once said something like, "just because it's beautiful doesn't mean it's right, but if it's right, it's probably going to be beautiful". Exceptions, to me, are not beautiful. (Mind you, templates, C++-style casts and namespaces aren't beautiful either, just look at all those angle brackets and colons getting in the way, but I'm quite happy to use them... Just one of those things I guess!).

I also, of course, have to be able to deal with them in other people's code, or if our team's coding standard were to include them.

Share this post


Link to post
Share on other sites
The thing is most functions should not require an explicit try/catch block. In most cases, all a function needs to do is make sure that the resources it has acquired are freed safely in the event of an exception. If you are using RAII then this is handled more or less automagically.

The exceptions (no pun intended) are functions that are intended to actually handle the exceptions. For example, recovery from a std::bad_alloc exception may be to retry an operation with a slower, but more memory friendly algorithm, or to retry after disabling certain subsystems. In that case you do need the try/catch block. However, in such a case it's probably less typing then propogating an out of memory error code up several function levels to a function that can finally handle the error.

Share this post


Link to post
Share on other sites
You can test what happens with classes that have explicit destructors. Set a break point in the destructor and see when and in what order they are called in.

Share this post


Link to post
Share on other sites
Just in case anyone was wondering why I don't like exceptions: I'm reading Herb Sutter's Exceptional C++, and Item 18 on page 60 shows a three-line function and asks "how many execution paths could there be through this function?"

There are three obvious ones, and TWENTY "invisible" ones that could be caused by exceptions. In THREE LINES.

Now, I'm not complaining about the fact that it could happen - if there are errors they need to be caught - but I don't like exceptions because I CAN'T SEE THEM HAPPEN, and it starts multiplying complexity way, way beyond most people's understanding.

I've no doubt they have their uses, but code should be as simple as possible (but no simpler, as the saying goes) and exceptions add to it's complexity.

It's a good book btw...

Share this post


Link to post
Share on other sites
Yes, but the point of writing exception safe code is so that you don't need to be able to identify each individual execution path. What matters is that you can look at a particular line of code and say 'if an exception is thrown on this line, all of the resources allocated before this line will be correctly released'.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!