Catch and Throw: Not Baseball, but C++ functions

Started by
10 comments, last by Zipster 23 years, 11 months ago
I found them in the help docs, and they seem to be confusing me. Can anyone offer any insite on ANYTHING about them? I already know they throw things like CMemoryException and stuff, whatever that means.
Advertisement
This is how the throw and catch stuff works... They are used when you want to throw or catch ANYTHING (everything).

First, you do this:

throw(something)
{
catch if (case):
{

}
...
else
pitch (exception);
}

Hope this cleared any of your questions.

More good news to come...
OldManDave.
thats nice, thx, but what do they actually do? from what i see, it just looks like a fancy switch statement.
Zipster: Somewhat like a fancy switch statement, but it allows you an opportunity to clean up, and to skip things when an error occurs. Say you need to call a slew of functions, and if one fails, you must cleanup and exit (do I hear initializing DX, anyone?). So, what you do is to write something like this:


void InitializeOS()
{
cout << "Initializing the operating system...\n";
}

void InitializeInput()
{
// deliberately throw an exception to demonstrate
throw "error occurred in InitializeInput\n";
}

void InitializeGraphics()
{
cout << "Initializing graphics...\n";
}

void InitializeSound()
{
cout << "Initializing sound...\n";
}

void InitializeGame()
{
InitializeOS(); // runs fine through here as usual
InitializeInput(); // we throw an error here
InitializeGraphics(); // code jumps right to the catch()
InitializeSound(); // so these 2 are never executed

catch( const char* pException ) {
cout << "An exception occurred; terminating program.\n\n";
cout << pException; // describes the particular error
}
}



It''s a bit long for an example, but basically it shows that when an exception occurs, the program keeps searching through each function for the catch() statement. If we didn''t have a catch statement in InitializeGame, then it would exit the function (as if we used return, and continue looking in the function that called InitializeGame, and so on. It continues like this... Also, any variables you allocated on the stack in any of those functions would be cleaned up while searching for a catch() block.


There are many uses to exceptions -- you place them whenever you might encounter something unpredictable. Kind of an awkward definition, but basically you just put them in key places (initialization and shutdown, mostly). They mainly allow you to clean up properly, where another error would just quit the program. You can also use them to test code, especially good when doing fullscreen DX -- you can set them up so that they automatically clean up DX when there''s an error that can''t be fixed while running. REALLY helpful!

Also, you can throw anything you like, with whatever description you like, as long as you have a catch() statement for that type. Classes, standard types, CProgram objects, you name it (I wouldn''t throw a CProgram object though, maybe just a pointer ).



- null_pointer
Sabre Multimedia
Whoa, thx for that detailed explanation! I think i understand now!
What happens if you try to catch the wrong type? Does it make an error or keep looking until there''s a match? Then you could have a bunch of catch statements for different types of errors and stuff.



Lack

Christianity, Creation, metric, Dvorak, and BeOS for all!
Lack
Christianity, Creation, metric, Dvorak, and BeOS for all!
LackOfKnack: If it can''t find a matching catch statement, it will keep looking, until it goes all the way up to main(). If it doesn''t find any even in main, the run-time library displays an "unhandled exception error". So, yes, you can do this:


catch( int x )
{
}

catch( char* pString )
{
}

catch( const char* pString )
{
}



Also, if for some reason you wish to catch all exceptions (perhaps in main()?), you can do this:


catch(...)
{
}



Note that with that approach, you can''t tell what type of exception was thrown and you cannot access what was thrown. You just know that an exception has occurred some place. Usually, though, people throw a string or an int that tells them what went wrong, so they only have to worry about catching one type.

Note that exceptions are not errors, but errors can be exceptions. Meaning, if you can easily predict the error, then there is a better solution to the problem, which is to stop it before it happens. Exceptions are kind of like your panic button -- if something happens that you can''t expect, you throw an exception and (if your program is structured correctly) everything will be automatically cleaned up.

What I mean by structured correctly, is using classes on the stack. Say you use a String class, which dynamically allocates memory and destroys it in the destructor.


void MyFunction()
{
String mystring; // doesn''t allocate anything yet

mystring = "Hello!"; // calls operator=, makes new copy

throw "Testing exception cleanup";

catch( const char* pExceptionDescription ) {
// just re-throw it up

// throw without params automatically
// re-throws the same object
throw;
}

// as the exception is thrown from this function,
// mystring''s destructor is called, and it deletes
// the char* that it allocated.
}

void main()
{
MyFunction();

catch(...) {
// catch anything, but we can''t use it
// we can at least clean up
}
}



This doesn''t mean much if the program is going to end, as all memory either on the stack or on the heap is automatically freed when the program exits. However, it pays dividends when you are working with APIs like Windows and DX which can actually crash if you don''t place the proper calls to clean them up properly. If those functions never get called, the resources stay "in use" and eventually Windows and DX will run out of resources to allocate. So if you wrap DX in a class, and create that class on the stack in main(), you will always be guaranteed that that class''s destructor will be called, even when an exception has occurred. No more having to reboot after 5 runs of your program!

Of course, that won''t help if you''re using NULL pointers (access violations), or just reading and writing to other invalid memory, unless you check the memory first and throw an exception if it''s invalid.

There''s also another exception thread which talks about when to use exceptions...


- null_pointer
Sabre Multimedia
Using Try( ){ }and Catch(...) is very useful for debugging programs that crash and kill the system.

If you put the code which you think might be killing the program within the {} of the Try, when it dies most of the time you can catch it, to verify if it was that code or not...


dan


Game production:
Good, quick, cheap: Choose two.
Game production:Good, quick, cheap: Choose two.
Game production:
Good, quick, cheap: Choose two.


haha i like that!

you might actually consider throwing something more useful like a structure.. and the function throwing the structure will fill in the details of the struct.. and the code that catches the struct can examine it and decide what to do.. it might be an error that is recoverable.. or maybe not..
if it''s recoverable.. you can recover and go on.. if not, rethrow the struct for the previous catcher to catch.

if nobody handles it.. then the program will terminate


adamm@san.rr.com
Thx guys, but i still have one last question: Can''t i just make a function called maybe ErrocFunc, and whenever an error occurs or the code fails, it runs this functions with a few parameters (for determining the error), and that function takes care of cleanup? I mean, instead of using catch and throw? Is that OK, too, or does catch and throw have certain advantages over a personalized function?

This topic is closed to new replies.

Advertisement