C++ Exception Handling

Started by
32 comments, last by ToohrVyk 17 years, 7 months ago
Hi all I am faced with a decision to take. I am to begin a new C++ project and I need to decide how to handle exceptions. As in should I continue with the normal return value indiactes error code mechanism or do i switch over completely to try - catch method of C++. I do feel the C++ method is good , but really dont know what the caveats could be. Please guide me regarding this. Also do I completely do away with the older method and use just the throwing exceptions method ? Are there any performance issues related to C++ exceptions ? Thanx in advance
Advertisement
Here is some tips:


  • Use exceptions when there truly are exceptional cases, not all errors are exceptions.


  • Write exception safe code, apply the Resource Acquisition Is Initialization (RAII) idiom and all other relevant variants of this idiom.


  • Aswell as exceptions use boost::optional, this is a more of a functional method of handling errors and should be part of every C++ coder's repertoire.


  • Use smart pointers and the standard library containers, these containers are guaranteed to have varying levels of exception safety.


  • Try to avoid error codes as much as possible, only when there really necessary (which is very rare).



[Edited by - snk_kid on August 25, 2006 10:36:31 AM]
Quote:Original post by _gl_coder_one_
Please guide me regarding this. Also do I completely do away with the older method and use
just the throwing exceptions method ? Are there any performance issues related to C++ exceptions ?


Using exceptions results in less writing (since you don't have to check all return values) and faster code when no exceptions are thrown (since you don't have to check all return values, too). When an exception is thrown, code reacts a little bit slower, but this should not occur often enough to worry.

Exceptions allow you to convey additional information about what error occured attached to the exception (which may be nested), and also allow use of the RAII idiom (which means you don't have to perform cleanup manually when an exception occurs). Exceptions also allow clean semantics by not relying on return values (it's kinda neat for a function to be able to return what it was intended to return).
I find that using exceptions makes the code much more readable. When updating some old code, I found myself replacing things like:

bool ok = DoSomething();if (ok) {    ok = AnotherThing();    if (ok) {        YetMoreThings();    }}with justtry {    DoSomething();    AnotherThing();    YetMoreThings();}catch(whatever) {}



which I think is easier to follow, without the error handling interspersed. The error return code version is probably only actually doing anything a small fraction of the time, yet causing a lot of clutter all the time.
Quote:Original post by snk_kid
Here is some tips:


  • Use exceptions when there truly are exceptional cases, not all errors are exceptions.


  • Write exception safe code, apply the Resource Acquisition Is Initialization (RAII) idiom and all other relevant variants of this idiom.


  • Aswell as exceptions use boost::optional, this is a more of a functional method of handling errors and should be part of every C++ coder's repertoire.


  • Use smart pointers and the standard library containers, these containers are guaranteed to have a varying level of exception safety.


  • Try to avoid error codes as much as possible, only when there really necessary (which is very rare).



Thanks for the boost::optional link - that looks like a useful library :-D

As an extention to the OP, is there any reason you should inherit from std::exception, and can you give an example of why you should do so?
Woooooooot!
There is also the option of having one catch block that triggers a general function for errors. Our engine "throws" errors and people register a callback function which is called with the error info (stored in a struct). This makes handling stuff from warnings, errors or other engine messages when something happens that shouldn't easy and in a central place.
The main issues with exceptions:

1) You need to use RAII if you want to stay sane. RAII is easy, however, and a good idea anyways. It's kind of like how picking your nose requires you to breathe, so as to supply oxygen to your arm and finger muscles. Personally, I'm going to breathe required or not for the functionality of my arm, because it's easier on my brain to have oxygen too.

2) ABI (not API) boundries. If you're building DLLs, you don't want to throw exceptions from them all the way up into the EXE typically, as unlike the C ABI, the C++ ABI is not standardized, so the only way to do this correctly is if both the EXE and DLL are built with the same compiler (and possibly compiler (sub)version). There's few reasons to have a seperate DLL in the first place in such a situation, though.

3) Performance. They're fast (especially when you're not actually throwing them), but you do not want to continuously throw them from the inner loop of your raytracer once per pixel.

--------------------

@ SouthernMonk: Yes. Simply put, std::exception is a standardized interface which every exception in the standard library derives from, providing you with a single, clean method of finding out what exception is occuring, in the event that you want to, say, display it to the user because you never coded anything to explicitly handle that exception (you frogot it, never expected it, or whatever).

In such a situation, if your exceptions do not derive from std::exception, you'd probably want to code a seperate catch statement for all the exceptions you didn't write yourself, and basically do the same thing with those exceptions as with your own - display what they were to the end user.

By instead deriving from std::exception, you can catch both your own exceptions and others in one statement. Further, others can catch your exceptions without requiring special knowledge of them.

-------------------

I typically code all my errors to be exceptions. Where I don't want to consider a failure in the callee-function to be considered an error in the caller function (for performance reasons or otherwise), I make it explicit an operation might not succeed in it's name. Contrary to what Yoda would have you believe, there is a try. I'll TryOpen() an optional config file, or AttemptLogin() from an interactive terminal. My automatic daily webcomic uploader will likely Open() the file with the login information required to Login() so it can Upload() the file. If any of this chain of operations fail, it cannot simply prompt me for the information - the entire task must be postponed until I notice, say, the email notification of the failure, in which case everything will need to be repeated from the start anyways.
So you should only throw exceptions when something unexpected occurs. That makes sense to me. But how do you decide what to throw? For example, couldn't you always throw std::exception's using the constructor that allows a c-style string as input? That way it becomes easier to log the error without any unnecessary exception classes and hierarchies.
Woooooooot!
Quote:Original post by SouthernMunk
So you should only throw exceptions when something unexpected occurs. That makes sense to me. But how do you decide what to throw? For example, couldn't you always throw std::exception's using the constructor that allows a c-style string as input? That way it becomes easier to log the error without any unnecessary exception classes and hierarchies.


In the header <stdexcept> there is an exception, std::runtime_error, which derives from std::exception and accepts a std::string as a constructor argument, and does just this. Example use: throw std::runtime_error( "I have no pants" );

And you've hit on the bingo keyword for the other half of the question. Necessity. If you're planning on doing more than just logging the information, chances are which exception was thrown will become important - and this is where having seperate types comes in handy. It's much easier to catch( file_does_not_exist ) { ... } rather than catch( const some_exception_base & bleh ) { if ( bleh.type == file_does_not_exist ) { ... } else { throw; } }.

This topic is closed to new replies.

Advertisement