exceptions

Started by
74 comments, last by aboeing 20 years, 6 months ago
Hi, I know there have been a number of previous threads on this topic, but I havent found any satisfactory explanations. The question basically is, why would you want to use C++ exception handling? (I understand that you want to use it for detecting very low level errors, but most of the time these errors dont happen - Im talking about as a general error handling mechanism) What advantages does it have over, say a global variable indicating whether an error occured and what it was? What advantages does it have over having a base class with an error variable, a pointer to its parent, and propogating the errors like that? With both these approaches you can simply put all your code somewhere and check for errors when its convenient, and dont need to worry about ugly nested try{}catch{} statements. Also: Do you always have to catch all your exceptions if you dont want your program to crash? (eg: loadtexture causes an exception, but its not critical, so I shouldn''t have to deal with it..) What are the performance issues related to exception handling? (If your already using virtual functions / RTTI is there added overhead?, Can exception handling be turned on/off for certain functions/classes?) What support does the OS need to supply for exception handling? (is it just a fancy term for an interrupt?) Thanks!
Advertisement
No one seems to answer, so I'll give it a shot...

What advantages does it have over, say a global variable indicating whether an error occured and what it was?

Simplicity. You don't have to check that global variable after every time you perform something and return some error value through a jungle of if (not error) 'do this' else 'do that' . You just do it (well, perhaps not just ). Also, some designs would not be possible without exceptions as a way of communicating an error; boost::lexical_cast would be an example. Let's say you are writing a templated container class, taking any type T. Inside the container, you make a copy of everything that is added to it. How should T tell the container and the user of the container that some error occured while copying? You could use some method is_ok() and return false, if T is a class, but that would rule out any primitive type and the container can only handle classes that implement is_ok(). It's not so generic any more. I don't know if my example was any good, but the point is that exceptions provide a standard way of handling errors. The container need only to provide some documented level of exception safety and leave the rest to the user.

Further, global variables has their own set of problems.

With both these approaches you can simply put all your code somewhere and check for errors when its convenient, and dont need to worry about ugly nested try{}catch{} statements

When it's convenient? Wouldn't it be convenient to handle errors when they occur? If it's ok for you to handle some errors when it's convenient, then don't use exceptions for those erros. Exceptions are best used for exceptional cases. This does not mean that it has to be some unrecoverable hardware error, when your home is about to explode, or etc, as I've gotten the impression that some might think. What's exceptional and what's not, depends on what you are doing. This does not mean that exceptions should replace simple return values.

Do you always have to catch all your exceptions if you dont want your program to crash?

Yes.

is there added overhead?

AFAIK, performance is only affected when setting up a try-catch block or when the exception is thrown.

What support does the OS need to supply for exception handling?

The OS has nothing to do with C++ exceptions. For Windows, there is also Structured Exception Handling, which is something different, but has a similar construct.

Edit: spelling

[edited by - Matsen on October 12, 2003 4:08:04 PM]
quote:Original post by aboeing
(I understand that you want to use it for detecting very low level errors, but most of the time these errors dont happen - Im talking about as a general error handling mechanism)

It *is* a better general purpose error handling mechanism.

Since it unwinds the stack EVERYTHING along the way can clean up or not handle the exception as it happens. Handlers can rethrow different exceptions or take actions like rollback a database to prevent destructive changes from happening.

A global variable holding the exception is okay, but then you have to make that local to the thread. Once you get around that, programs don''t have to check it, and it encourages a sort of irresponsible coding style where "this works most of the time." Then when something doesn''t work, go run post on Gamedev! If you don''t have an exception then your program stops RIGHT there and you can look at the exception and determine what happened if you provide enough information.

Also, using return values to indicate success/failure really isn''t necessary nowadays. Your ''nominal'' code path does not turn into a gigantic indentation because of all the if''s. The error handling logic is separate from the normal logic.
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
quote:Original post by Matsen
What support does the OS need to supply for exception handling?

The OS has nothing to do with C++ exceptions. For Windows, there is also Structured Exception Handling, which is something different, but has a similar construct.

On Windows, C++ exceptions are implemented on top of SEH.
--AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.[Project site] [IRC channel] [Blog]
quote:On Windows, C++ exceptions are implemented on top of SEH.
Yes, that''s true for a Microsoft compiler, but what about other compilers, such as gcc?
The question basically is, why would you want to use C++ exception handling?

Because it is easier and safer than propagating errors by hand.
Exceptions propagate until they find the appropriate handler.

What advantages does it have over, say a global variable indicating whether an error occured and what it was?

Exceptions are always detected. Programers quite often forget to check error codes.

What advantages does it have over having a base class with an error variable, a pointer to its parent, and propogating the errors like that?

You can already arrange exceptions in a hierarchy and catch the base exceptions. I don''t quite understand the approach you are suggesting ? Is the base class an exceptiobn class ? Is it a base class of the objects you manipulate (setting the error member when an error occurs) ?

With both these approaches you can simply put all your code somewhere and check for errors when its convenient, and dont need to worry about ugly nested try{}catch{} statements.

Instead you will have to worry about whatever structure YOU put in place to emulate exceptions. Personally, I prefer using a construct that is directly supported by the language than a half-baked, untested alternative that may require me to actually do more work.

If you used the RAII idiom ( "Ressource Acquisition Is Initialisation" ) - Acquiring resources in constructors, releasing them in destructors, you don''t even have to worry about things like closing files, releasing locks, freeing memory blocks ...

Do you always have to catch all your exceptions if you dont want your program to crash?

Yes

(eg: loadtexture causes an exception, but its not critical, so I shouldn''t have to deal with it..)

If it is not an error, don''t throw an exception.

What are the performance issues related to exception handling?

None unless an exception is actually thrown, at which point you shouldn''t care for performance anyway - something wrong happened, you need to fix it before moving on. try is practically free, it is throw that is expensive.

Can exception handling be turned on/off for certain functions/classes?

Not in standard C++. Your compiler may let you do it as an extension.

What support does the OS need to supply for exception handling?

I don''t know.

(is it just a fancy term for an interrupt?)

No. Exceptions trigger a stack unwinding within your program until the appropriate handler is found (or the exception leaves main(), at which point terminate() is called). Interrupts suspend the execution of your program to execute a handler that ''lives'' outside of your program (possibly in kernel-land).

[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Hi,
First of all, thank you everyone for your feedback!

Re:Matsen:
"Simplicity."
-> to me, exceptions seem less simple.
try {x;} catch() {}
is more code than:
x; if(error) {}

"If it's ok for you to handle some errors when it's convenient, then don't use exceptions for those errors"
This is what I figured, in which case I would need my own error handling system anyway, so why bother with exceptions?

"AFAIK, performance is only affected when setting up a try-catch block or when the exception is thrown."
Well, this is good, at least I dont have to be afraid of using them =)

Re:antareus:
"Also, using return values to indicate success/failure really isn't necessary nowadays. Your 'nominal' code path does not turn into a gigantic indentation because of all the if's. The error handling logic is separate from the normal logic. "
How do you seperate error handling from normal code with exceptions? Are you just saying that try{}catch{} is seperating it? It would seem nicer to have a seperate HandleError method for each class that would be automatically called if something went wrong.

Also, as I suggested, your error variable could maintain information as to where the error occured (__LINE__,__FILE__ etc) so I dont see the point of using exception handling for this reason.

Re:Fruny:
"Because it is easier and safer than propagating errors by hand."
Is this safer meaning that I'm likely to make a mistake in my error handling code, or do you mean something else?

"Exceptions are always detected. Programers quite often forget to check error codes."
This is also one of my major concerns about exceptions. Writing code like try{CreateDXEngine();}catch(){} try{CreateTexture();}catch(){} try{CreateMesh();}catch() try{Render();}catch() seems like a very ugly way for the end user to use a system.

"Ressource Acquisition Is Initialisation"
Agreed that exceptions would work quite nicely in this design, but I dont think its possible to develop my entire system this way.

"If it is not an error, don't throw an exception."
But what now? now I need some kind of not-exception error handling mechanism, and as I said before, doesnt this make exceptions redundant?

"Exceptions trigger a stack unwinding"
Sorry if I dont really understand, but does this mean extra information needs to be passed on the stack with every function call/whathaveyou?

-=-=-=--=-=-=--=-=-=--=-=-=--=-=-=--=-=-=--=-=-=--=-=-=--=-=-=-

From the overall response I can see that people like exceptions because they are language specific and they force users to deal with errors that happen. (personally, I'm not a fan of this, as users generally dont deal with errors, and I would prefer for my program to continue trying to run than have it exit and say "exception thrown blablabla")

The responses indicate that there are no performance reasons to not use exceptions, so I should use them.

Also, people say only to use exceptions for exceptional circumstances (makes sense.)
So this means you'll want some other method of error handling aswell. So, where do you draw the line? And what is the best method for doing so?

What I had in mind (Re:Fruny:"the approach you are suggesting?" ) was something like:
class Base {struct error_struct; //contain info such as __LINE__,etc.Base *parent; //point to my 'creator'void SetError(an_error_struct) {this->error_struct=an_error_structparent->notify_an_error_occured_and_call_set_error_if_appropriate}}class Texture{void Loadtexturefromfile(){if(filenotfound) SetError();}}class Model {void SetupModel() {Texture *mytex = Create("Texture",this); //automagically set parent//Load a bunch of texturesIf(erroroccured) {deal with it, if the texture wasnt critical to gameplay we can render the model anyway }}}

you get the general jist of it.. (you could easily extend this so that the base class had a HandleErrors() method that was automatically called as the error propogated..)

Thanks.

(edit:fixed italics)

[edited by - aboeing on October 13, 2003 8:57:03 AM]
What about this code then:
int* x1;int* x2;int* x3;int* x4;int* x5;try{  x1 = new int;  x2 = new int;  x3 = new int;  x4 = new int;  x5 = new int;}catch(...){ std::cout << "there was an error..." << std::endl;}If you would write the same code with if''s :int* x1 = new int;if(x1 == NULL) std::cout << "there was an error" << std::endl;int* x2 = new int;if(x2 == NULL) std::cout << "there was an error" << std::endl;int* x3 = new int;if(x3 == NULL) std::cout << "there was an error" << std::endl;int* x4 = new int;if(x4 == NULL) std::cout << "there was an error" << std::endl;int* x5 = new int;if(x5 == NULL) std::cout << "there was an error" << std::endl; 
quote:
deal with it, if the texture wasnt critical to gameplay we can render the model anyway


!!!LMAO!!!
AP #1:
True, perhaps exception handling should be used for out-of-memory checks aswell, although in my experience I dont often need to do something like that. (most of the time my classes only have 1 or 2 non-class objects that need to be created).

Then again, do we consider out-of-memory an exceptional case?
I probably would. The other instances I would use it are for standard functions which could fail, or when you are calling a users-plugin which could have bad code in it. (naturally, all the code I would write would work perfectly, so this wouldn''t be necessary for me ) Any other opinions?
(eg:
FILE *fp;
try {
fp = fopen(filename, "a");
} catch (...) {
#ifdef WIN32
MessageBox(NULL,"Serious error has occured, could not call fopen!","Log::debug",MB_OK|MB_ICONWARNING);
#endif
return;
}
if (fp==NULL) return some_error;
)

AP #2:
Think of a quake3 map, and not all the textures are available for the map, the engine should still load and render what it can.

This topic is closed to new replies.

Advertisement