Jump to content


C++ exception handling alternatives


35 replies to this topic

#1 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 03 April 2006 - 10:43 PM

Hi all,

I'm sick of exceptions. It devours memory, slows down my applications, and terminates them when they could have continued without a problem. Okay that last thing is probably my fault because I should catch and handle each and every one of them, but it's still a pain. So should I just use good old assert? All I'm using exceptions for is to catch (pun unintended) development errors anyway. In release builds they're not helpful at all.

But I'm hesitative and looking for some expert advice. Can anyone honestly tell me they need exceptions badly? Or did anyone get rid of them and gained in performance and stability without regrets? Note that for my applications performance is of very high importance and keeping things running with minor flaws is way better than terminating the application with a 'friendly' notification.

Thanks,

Nick

P.S: Actually I've already made up my mind a while ago but I just want new opinions.

#2 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 03 April 2006 - 11:24 PM

I think you should not throw exceptions for programming errors, that's a fine job for assert. You should throw exceptions for system errors that you like to handle, like losing a database connection or reading from a file. And these are of course exceptions that need to be caught at some point in your code, but I think they're better than return codes as you can choose where you want to handle them. Things like clean-up and rollback actions should be implemented using destructor code anyway so try-catch-rethrow actions should be unnecessary
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#3 George McBay

    New Member

  • Members
  • Pip
  • 2 posts

Posted 03 April 2006 - 11:29 PM

Exceptions don't use a significant amount of memory or slow down your application at all unless they are thrown. If you're throwing them in cases that aren't EXCEPTIONal (like, using them as a substitute for method return values), you're using them wrong. If you're throwing them and not catching them, you're using them wrong.

If you'd really rather have a "high importance" bit of software keep running with 'minor flaws' rather than just have it crash outright with a detailed error report, you scare me and I hope you never write software that my life depends on. The same basic memory corruption problem could be "minor" one run and seriously trash your data on another -- there really isn't any such thing as a "minor flaw" in this area.

I kind of get what you're going for, but if that sort of always-on uptime is your goal you should still let the application crash and just have some background application manager start a new instance when it notices that the previous one crashed.

I think the exception debate had merit back in the days when most compilers did a bad job implementing them, but these days they work perfectly fine in gcc and MSVC++, one or the other of which a huge percentage of the C++ programmers out there will be using.

If you really don't want to use exceptions in C++, you don't have to. Do things the old fashioned C way. Its your choice. But IMO exceptions are extremely useful and I use them a lot.

#4 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 03 April 2006 - 11:37 PM

.oisyn said:

I think you should not throw exceptions for programming errors, that's a fine job for assert. You should throw exceptions for system errors that you like to handle, like losing a database connection or reading from a file. And these are of course exceptions that need to be caught at some point in your code, but I think they're better than return codes as you can choose where you want to handle them. Things like clean-up and rollback actions should be implemented using destructor code anyway so try-catch-rethrow actions should be unnecessary
Thanks! Considering that I don't use any database connections and hardly any file reading I should probably be fine with just assert. :yes:

#5 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 03 April 2006 - 11:54 PM

George McBay said:

Exceptions don't use a significant amount of memory or slow down your application at all unless they are thrown.
You should do more research into the runtime and compilation cost of exceptions. While it may not be much, you pay. Because every function might throw an exception, and any object created in-between function calls needs to be destroyed. Destructor calls need to be registered at runtime, so the stack unwinding process knows which objects to destroy. This is runtime cost, but there's also compilation cost: because every function may throw, some assumptions can't be made and this will reduce the amount of optimization.

We compile all our games with exceptions turned off, this gives us a pretty good overall speed gain (MSVC++ 7.1 that is for all MS platforms, don't know which gcc we're using for the PS2 though). And besides, there is not much reason to throw exceptions in games anyway as you can mostly assume things will work, especially on a console.

(On another note, MSVC++ has the quirck that with exceptions enabled it isn't able to inline functions that return or receive unwindable objects by value, like the __m128 SSE type)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#6 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 04 April 2006 - 12:02 AM

George McBay said:

Exceptions don't use a significant amount of memory or slow down your application at all unless they are thrown.
It depends on what you call significant of course. Every function containing try/catch/throw has extra instructions and stack elements that can be a real overhead. Like the difference between an inline call and a regular call, or a regular call and a virtual call. It does make a difference in high-performance applications.

Quote

If you're throwing them in cases that aren't EXCEPTIONal (like, using them as a substitute for method return values), you're using them wrong.
Innocent.

Quote

If you're throwing them and not catching them, you're using them wrong.
Guilty. But C++ isn't really assisting me with that, and often I just don't want to be bothered with writing the handling code unless that exception does get thrown one time...

Quote

If you'd really rather have a "high importance" bit of software keep running with 'minor flaws' rather than just have it crash outright with a detailed error report, you scare me and I hope you never write software that my life depends on.
I wouldn't consider a game showing minor artifacs life threatening. It's much better to have a visual glitch at the end of a hallway then to just terminate the application. Coincidentally, my brother was playing Half-Life 2 yesterday and it kept crashing (hard) when he shot at a certain enemy with the gravity gun. Visual Studio showed that it was an unhandled exception in the application code. Whatever was the real problem, I'm pretty sure that if it didn't throw an exception it wouldn't crash.

Quote

The same basic memory corruption problem could be "minor" one run and seriously trash your data on another -- there really isn't any such thing as a "minor flaw" in this area.
In that case exceptions aren't really helping anything. I'm sure it happens a lot that exception handling code actually contains flaws. However minor code-wise, you'll only find out the consequences when the exception gets thrown. Which might never happen during your own tests...

Quote

I kind of get what you're going for, but if that sort of always-on uptime is your goal you should still let the application crash and just have some background application manager start a new instance when it notices that the previous one crashed.
Sorry, I wasn't entirely clear there. There's no strict need for 100% uptime. It's just that of the 100 exceptions that can get thrown, 90 or more are likely to cause more trouble than compared to just ignoring the error condition completely (like assert would do in release build).

Quote

I think the exception debate had merit back in the days when most compilers did a bad job implementing them, but these days they work perfectly fine in gcc and MSVC++, one or the other of which a huge percentage of the C++ programmers out there will be using.
Sure, but I think too many people regard them as the one and only error handling method, and think their application is magically safe because they use exceptions. So I just think they are overrated and there are applications that don't use the alternatives appropriately or would be even better off not using exceptions at all.

Quote

If you really don't want to use exceptions in C++, you don't have to. Do things the old fashioned C way. Its your choice. But IMO exceptions are extremely useful and I use them a lot.
Can I ask where you would use them in a typical game engine? Any modules where you would avoid them, and other places where it's the superiour error handling method?

Thanks!

Nick

#7 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 04 April 2006 - 12:08 AM

.oisyn said:

We compile all our games with exceptions turned off, this gives us a pretty good overall speed gain (MSVC++ 7.1 that is for all MS platforms, don't know which gcc we're using for the PS2 though). And besides, there is not much reason to throw exceptions in games anyway as you can mostly assume things will work, especially on a console.
Thanks a lot .oisyn, that's the kind of information I was looking for! :worthy:

#8 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 04 April 2006 - 07:40 AM

Up to now, I just used exceptions for _really_ critical conditions I was too lazy to check for each time they might occur.

An example is the malloc call. I built my own wrapper for it which would throw an exception once an allocation failed. Then, I have a single try-catch-clause around my entire program, and wait for those critical exceptions there, rather than check for (pointer == Null) on each allocation.

However, in the more recent time, I think even that might not be really neccessary, since I could just add a simple message box and an exit statement in the allocator function.

Another argument against exceptions is that there rarely should be an occasion where an error deeply within nested code should be handled somewhere high above. From my point of view, it's simply a better idea to have a normal error return value, and react to it in a way that doesn't make the programm crash, but rather just fail gracefully, and continue as far as possible.

About performance, well I'm not too sure, but I think I had weird problems with VC6 with exceptions turned off, think it was something about some libraries requiring them, but I haven't looked into that much, and just left them enabled. However, if they are used by code outside your control, I guess you will still get a performance penalty.

Just my thoughts,
Cheers,
- Wernaeh

#9 Alex

    Valued Member

  • Members
  • PipPipPip
  • 152 posts

Posted 04 April 2006 - 09:16 AM

I'd say the most importand thing is to be consistent when handling errors. You don't wonna randomly switch from exceptions to return values etc etc.
There is places where you can hardly avoid exceptions (like guarding your vm that executes scripts). Other than that you can use events or something the like to handle exspected problems (like loosing a connection).

Personally I find it a pain to handle EVERY return value as an exception (like java). I throw only if an illegal error condition has occured or the condition cannot be handled in any way to resume the app (heap corrupt/out of mem/...)

Alex

#10 bramz

    Valued Member

  • Members
  • PipPipPip
  • 189 posts

Posted 04 April 2006 - 10:01 AM

Nick,

For games and stuff, I agree a small glitch is better than a crashing game. As mentioned before, use assert for most logic errors (errors caused by wrong use of a function), and something else for most runtime errors (errors caused by bad input or other external causes).

Myself, I use exceptions for runtime errors, because (a) about all code I write is used as a module for a scripting language. So, when an error happens, I mostly let it bubble up to the script (what would happen anyway if everything was written in that scripting language). And (b) more importantly, in my case correct results are more important than not crashing ... what makes it a completely different story.

So as always, the correct answer depends on the situation ;)
hi, i'm a signature viruz, plz set me as your signature and help me spread :)
Bramz' warehouse | LiAR isn't a raytracer

#11 oxymoron

    New Member

  • Members
  • Pip
  • 7 posts

Posted 04 April 2006 - 12:18 PM

bramz said:

As mentioned before, use assert for most logic errors (errors caused by wrong use of a function), and something else for most runtime errors (errors caused by bad input or other external causes).

In general it's a bit more complicated than that. Suppose you're writing a library that will be supplied as a .lib/.dll to your client. Then any fault trapping that dissolved (ie assert) might actually cause a crash in release build - you just don't know what sort of input a library will have. So in a lib I tend to use exceptions rather than asserts. OTOH if you are supplying libs as source you can probably get away with asserts.

In an application you know exactly what's going into a function so assert works better. In fact you can do this in a library to within implementation-specific methods (ie ones with no public access).

Internal state checks are also good candidates for assertion. Often I implement a brute-force version of an algorithm first and then later switch in a better one. Keep the brute-force one in debug builds, apply both algorithms and assert the results are the same.

For console game development, exceptions are often passe. Memory is at a premium, and the performance hit is a big factor too. So in a single console or multi-platform development system, exceptions tend to get squeezed out in favour of assertions. If this is the case, then functions that can return an error should, and if it does then you are duty-bound to test that value. Game code that runs on a console with no hard-drive needs to be bullet-proof, so some compromise between efficiency and robustness needs to be made.

#12 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 04 April 2006 - 12:29 PM

bramz said:

For games and stuff, I agree a small glitch is better than a crashing game. As mentioned before, use assert for most logic errors (errors caused by wrong use of a function), and something else for most runtime errors (errors caused by bad input or other external causes).
I mostly agree, but I have an anecdote about user input checking. A couple of months ago I was working on a university project with lots of data being read from files. One specification explicitely stated that a certain bit in a structure is reserved and always 0. So I blindly checked for that and threw an exception in case of any... exception. Despite the specification (which was the last version) it did occur for one file. And for some reason my compiler wasn't catching any exceptions, so they were handled a little lower down the call stack messing things up. And because it was a huge dataset I spent several hours debugging. Ironically, not throwing an exception solved everything, but it is without a doubt a user input error.

Well, I guess it's also a case of throwing an exception and not correctly handling it, or using exceptions where other approaches would have worked better. Anyway, I'm now convinced that for my applications exceptions are evil and should be avoided when possible.

The only place where I still use exceptions is at public interfaces that return a success/failure code.

#13 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 04 April 2006 - 01:17 PM

Quote

because it was a huge dataset I spent several hours debugging
Also, you should use a decent debugger that allows you to break on the throw statement, rather than letting it being caught by the program itself ;) (MSVC++ can do this, to name one)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#14 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 04 April 2006 - 03:16 PM

.oisyn said:

Also, you should use a decent debugger that allows you to break on the throw statement, rather than letting it being caught by the program itself ;) (MSVC++ can do this, to name one)
I know, but the debugger was configured incorrectly and I wasn't aware of that. And I was looking for a logic bug in the processing, not expecting anything exception related (because I assumed the debugger would catch them).

Anyway, that's obviously not an argument against exceptions. But imagine that I didn't have that one particular file that triggered the exception, but some 'end users' do get wrongly processed data... So exceptions should definitely be avoided in many situations.

One small extra question: Is there any C++ style assert? I have the impression that assert is considered C style and many programmers avoid it because they think exceptions can be used as superiour C++ style asserts. Are there any error handling constructs I might not know about yet?

#15 bramz

    Valued Member

  • Members
  • PipPipPip
  • 189 posts

Posted 04 April 2006 - 03:34 PM

There are the 'enforcers' by Alexandrescu, but that's an exception-based always-enabled assert style thing. So, probably not what you're looking for ;)

About throwing exceptions, I use a macro that adds __FILE__ and __LINE__ to the exception message, so that I at least know where the exception comes from. Then, even with a malconfigured debugger, I at least know where to look ... Of course, this also means to you have to wrap your main() in one big try/catch block to print the actual message.

You mentioned one anecdote where ingoring a user input error would not have harmed, while throwing an exception costed you a few hours of debugging. But there are an equal amount of anecdotes that will tell just the opposite.

So, as with everything: it's not because you have a hammer that everything is a nail, but not everything is a screw either.
hi, i'm a signature viruz, plz set me as your signature and help me spread :)
Bramz' warehouse | LiAR isn't a raytracer

#16 .oisyn

    DevMaster Staff

  • Moderators
  • 1842 posts

Posted 04 April 2006 - 03:38 PM

I think the assert macro is perfectly fine in C++. You might consider creating your own so you have more freedom (like printing a stack trace, asking whether to retry, ignore or break into the debugger, and having different levels or groups of assertions that you can turn on and off using specific build configurations)
C++ addict
-
Currently working on: the 3D engine for Tomb Raider.

#17 bramz

    Valued Member

  • Members
  • PipPipPip
  • 189 posts

Posted 04 April 2006 - 03:46 PM

Besides, not using exceptions will not guarantee you that the application will continue with only a glitch.
hi, i'm a signature viruz, plz set me as your signature and help me spread :)
Bramz' warehouse | LiAR isn't a raytracer

#18 juhnu

    Valued Member

  • Members
  • PipPipPip
  • 292 posts

Posted 04 April 2006 - 03:47 PM

Nick said:

Anyway, that's obviously not an argument against exceptions. But imagine that I didn't have that one particular file that triggered the exception, but some 'end users' do get wrongly processed data... So exceptions should definitely be avoided in many situations.

Interesting logic. Even you say it's not an argument against exceptions, you are using it as a reasoning for not using them. This your problem has nothing to do with the exceptions.

1. You should know what your compile settings are
2. you should handle the user input error somehow, was it exceptions or any other method it doesn't matter.
3. You should test your program always with malicious data/user input to see if your error handling is actually working.

You don't blame your car either if you fill the tank with baby oil..or do you?

#19 monjardin

    Senior Member

  • Members
  • PipPipPipPip
  • 1033 posts

Posted 04 April 2006 - 04:13 PM

"juhnu" said:

You don't blame your car either if you fill the tank with baby oil..or do you?
No, but I do have a coworker that is running his truck on waste oil from Taco Bell. ;)

I've found asserts much easier on the fingers for handling programming errors that shouldn't make it into the final product. Is there any drawback to making overzealous use of assertions?

As to exceptions, I would never attempt to add them to code that wasn't built with them in mind from the beginning. If you are careful with them, and only use them in the proper context, then they can be lovely. :)
monjardin's JwN Meter (1,2,3,4,5,6):
|----|----|----|----|----|----|----|----|----|----|
*

#20 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 04 April 2006 - 04:40 PM

juhnu said:

Interesting logic. Even you say it's not an argument against exceptions, you are using it as a reasoning for not using them. This your problem has nothing to do with the exceptions.
In the original situation I can't blame the exceptions, it was the debugger's fault or I got the setting wrong. But if I didn't have a data set that triggered the exception there would still be a bug (actually strictly speaking it's a user error if they don't comply with the specification). And I can blame the over-eager use of exceptions for that. Obviously every bug is a programmer bug if I shouldn't blame my tools. I'm just pointing out a flaw in C++ exception handling, because a language like Java explicitely forces me to handle the exception or add it to the function declaration. That makes it very unlikely that I would forget to handle it properly (or throw it in the first place).

Quote

1. You should know what your compile settings are
Sure. And what if it's not my compiler and not my PC?

Quote

2. you should handle the user input error somehow, was it exceptions or any other method it doesn't matter.
Even someone with 10 years of C++ experience could forget to handle an error.

Quote

3. You should test your program always with malicious data/user input to see if your error handling is actually working.
It was out of specification. The actual error was in the user input or code that generated the data to be processed. Besides, I only got one afternoon.

Quote

You don't blame your car either if you fill the tank with baby oil..or do you?
I blame the guy who filled the tank with baby oil, because it wasn't me. :whistle: Would you blame the engineer that your engine gets filled with baby oil? What I did was try to check for the proper fuel but it turned out to be an explosive mixture...





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users