Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

[C++] Exception handling

Started by ppetruzalek Jun 15, 2010 at 3:13 PM 10 replies 2.4k views
Original Post
ppetruzalek
ppetruzalek
Hi. I'm trying to improve my coding skills by using exception handling on my game. But I got a few doubts that I would like to ask the wiser:

When I throw an exception in the constructor of an object, it destructs (by calling the destructor) the object which thrown the exception?

Let's say: I have an Image class within a Sprite class within the Game class. If the Image() constructor is called from the Sprite constructor, and the Image constructor throws an exception, the exception handling will destroy both the Image and then the Sprite?

Image::Image(){   m_pSurface = SDL_CreateRGBSurface(...);   if( m_pSurface == 0 ) throw( SDL_GetError() );}Sprite::Sprite(){   Image m_pImage = new Image;}Sprite::~Sprite(){   delete m_pImage;}Game::Start(){   try   {      Sprite pSprite = new Sprite;   }   catch(char* e)   {      std::cout << e << std::endl;   }}


Let's say that code above raises an exception (char*) because SDL_CreateRGBSurface returned an error. What is the expected behavior of m_pImage? It will be NULL? Or I will have to catch the exception in the Sprite constructor, free the memory of m_pImage and then rethrow it to the game class to catch it? Or the destructor will do the work for me?

This way I'm using exceptions is a good or a bad way of using it?

Thanks in advance.
SiCrane
SiCrane
When an exception is thrown inside the body of a constructor, the destructor for the class is not run, though the destructor for any member variables and base class subobjects will be executed. If the exception is thrown in a constructor that is called as part of a new expression, a corresponding call to the matching delete function will free the memory allocated for the object.
GregMichael
GregMichael
Just out of interest..how many commerical games use exception handling ?

ppetruzalek
ppetruzalek
SiCrane: so If I had something like this in the Sprite class:

Sprite::Sprite(){   m_pArray = new int[100];   m_pImage = new Image();}


And if Image throws an exception, that means that m_pImage will be freed, but I will have a leak in m_pArray, right?

So in this case I should do:

Sprite::Sprite(){   try  {     m_pArray = new int[100];     m_pImage = new Image();  }  catch(char* e)  {     delete[] m_pArray;     throw;  }}


This code should prevent a leak from the allocation of array and re-throw the exception to the owner of Sprite object to handle. Is this the correct approach? Thanks again.

GregMichael: I don't know the answer to that either, but most of the C++ programmers i've known didn't even knew that there is exception handling in C++. I've only learnt about it when I meet a GREAT C++ programmer that told me about it, but he had a good background in Java too. Since I'm from the GOTO era and jump-over jump techniques to avoid far jump, and weird stuff like that, now I'm trying to improve my code with modern error checking and flow control so that's why I am asking.
GregMichael
GregMichael
My only reason for asking is...I've (personally) never known any (commercial) game code that uses Exceptions, but I'm sure there are lot's that do I've just never seen it myself...I must get out more :)

I'm probably wrong (in my own limited experience)...and will get corrected I'm sure.

To improve your coding skills...write a game or fifty and tools as well.

Do that and your coding skills will be much improved.

Don't worry too much about the nitty gritty of the language(s)...just write a game.

And the tools to support those games - tools are a great test of programming skills or lack of - how does an artist or designer want to edit this game level...what would be easiest for them ?

Then worry about if you need to handle exceptions...

What I'm getting at is "Don't get bogged down in the language"...write a game.

Then you'll know what you need to support.





ppetruzalek
ppetruzalek
Thanks for your contribution Greg. I've done a lot of professional work in my life, but never programmed a game before, and in those projects I've never used a true C++ approach. I've just used it as a C with classes, and classes being "structs with functions embedded". I've hardly ever used any more advanced techniques like templates, inheritance and exception handling. Now I'm building a game for the pleasure of doing it, and since I also like to evolve my skills from project to project, I'm taking the chance to study this stuff I never paid much attention.

Probably in a commercial game the programmers have such pression from the deadlines and stuff that they don't bother writing a so "by the book" code, but since I'm not under that kind of pression I think it's a good opportunity to try something new.
TomH
TomH
Hello ppetruzalek,

Leaving aside the questions about whether exception are the right tool for this job, the "standard" approach to this problem is using two library classes: std::auto_ptr and boost::shared_array.

The basic idea is that as an object's constructor is executed, any resource that are allocated is stored via auto_ptrs on the stack. The auto_ptr provides the behaviour that when it goes out of scope (is destructed) it will destruct the object to which it points.

This in combination with the fact that throwing an exception causes the stack to be unwound means that if an exception is thrown within the Image constructor (and not caught by the Sprite constructor). The stack based auto_ptrs go out of scope and these in turn call destructors for any resource that has already been allocated.

The only piece that is missing is what happens if everything works. In this case once all the allocations have completed then resources are assigned from the auto_ptrs into member variables. At the point of assignment the auto_ptr stop managing their resource (so they don't destruct resources when the constructor ends normally)


As an exercise for the reader take a look at:

auto_ptr info
shared_ptr docs
shared_array docs

I don't have access to a compiler at the moment, but the code should look roughly like this:
class Sprite{  private:        boost::shared_array<int> m_pArray;    boost::shared_ptr<Image> m_pImage;...Sprite::Sprite(){  // Perform all the actions that can fail (throw), storing in an auto_ptr on the stack  std::auto_ptr<boost::shared_array<int> > array ( boost::shared_array<int>>( new int [100] ) );  std::auto_ptr<boost::shared_ptr<Image> > image ( boost::shared_pointer<Image> ( new Image ) );  // Now perform assignments from the stack based auto pointer into the class, until the constructor  //  completes don't call anything that can throw  m_pArray = array;  m_pImage = image;}Sprite::~Sprite(){  // Releasing m_pArray and m_pImage is done by the boost::shared_array and boost::shared_ptr}
ppetruzalek
ppetruzalek
Thank you very much Tom. I guess I will have to study a lot more than I thought before I can insert exception handling in my code. I probably will have to change the way I am modelling the application as well, so for now I'll take Greg's advice and just write the game, since it's my first. In the next iteration I will expend more type in the modelling stage to enable exceptions, because if I try to do it now it will probably mean that I will have to refactory a significant part of my code.
TomH
TomH
Fair enough, I think that's a good decision.

For the record I should probably correct a few point in my original reply. When using shared_ptr and shared_arrays, you don't need the auto_ptr. A local shared_ptr on the stack will ensure resource are correctly released. This would make the example something like:

class Sprite{  private:        boost::shared_array<int> m_pArray;    boost::shared_ptr<Image> m_pImage;...Sprite::Sprite(){  // Perform all the actions that can throw, storing allocated resources   //  on the stack in a reference countered smart pointers  boost::shared_array<int> array ( new int [100] ) );  boost::shared_ptr<Image> image ( new Image );  // Now perform assignments from the stack based smart pointers into   //  class members. Once starting these assignments don't call anything   //  that can throw  m_pArray = array;  m_pImage = image;}Sprite::~Sprite(){  // Releasing m_pArray and m_pImage is done by the boost::shared_array and boost::shared_ptr}
Zahlman
Zahlman
Like TomH said, but also:

Quote:
catch(char* e)


You should never have this in modern code (except to deal with somebody else's ancient garbage). If an exception is thrown by 'new', it will not be of type char*, but instead std::bad_alloc. Also, it is usually best to catch exceptions by reference, thus
catch (std::bad_alloc&)
. (Also notice that you do not need to provide a name for the thing you're catching if you are not going to refer to it in the exception-handling code.)

Similarly, do not just throw the char* returned by SDL_GetError(); instead, wrap it in one of the standard exception classes provided e.g. std::runtime_error (if nothing more specific seems to be appropriate).
GregMichael
GregMichael
I come from an assembler / C background...so I'm still learning C++ and the right ways of doing things. Learning is good though !

But I still believe...write the game first...learn from your mistakes, don't make them again - and the next game will be better.

It doesn't matter what language the game is written in, or what rules you followed to get the game done, if X number of people download or buy your game then you've achieved something (it depends on what X you want to make you happy :)

After all, if I write a game that no-one else wants to play then what's the point ?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.