• Advertisement
Sign in to follow this  

C++: Exception Handling - How to avoid leaks.

This topic is 3480 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm quite new to Exception Handling. I resisted the idea for a long time because it seems to me that it is easy to overlook memory leaks and other state issues when exceptions are thrown. I'm not really sure that I believe try/throw/catch is better than returning error codes, but I'm trying it out. My general question is: If a function is partway through creating a collection of objects on the heap when an exception is thrown, is there a better way of handling this then simply writing try/catch blocks around every single function call that can throw so that I can clear my heap-allocated objects before I throw-exit out of the function? I guess this is still more efficient than checking return codes, but it's far bulkier to code. My current ideas are: 1. Design the function so that all heap objects are pointed to by a function-local object that will destroy them (such as my heap-allocated token tree below having a function-local stack-allocated root). 2. Create some sort of pointer template class that deletes on destruction, and clear it before a natural function exit. (Seems like an ugly hack to me). 3. Wrap the function in a single try/catch block. In the catch block free all loose objects, then re-throw. This is my current line of thought, although it places many restrictions on my code, and requires most of my functions to have try/catch blocks. The current scenario is: In my program's UI you can open a file. This file is opened using ifstream, parsed using a linear recursive descent parser, and converted to data. If any of these components fails (eg the file doesn't exist, or the data doesn't fit the grammar), I'm throwing an error (or ofstream is via std::ofstream::exception()). The program catches the error and displays a popup on the UI, but doesn't exit the program. Now, in my parser I'm building a tree of tokens (unlimited recursion). Each token object can have unlimited children, and destroys them in the event of it's own destruction. Each time a token is read and the parser ascends (toward the root), it is either deleted or added to the token below it. There is a vector of pointers to tokens, one for each depth in the tree at the current point in the file being parsed. This means, if I'm 5 tokens deep, I have 5 tokens on the heap that are currently only referred to by my vector of pointers. After parsing completes, all parsed tokens have either been discarded and deleted, or are part of the final tree. My current thinking is to add the heap-allocated root token to a function-local stack-allocated super-root token, have every token currently being parsed added to it's parent (whether or not it is actually accepted), and adding a method to tokens that will orphan the last added token (or a specific token). This way, in the event of a throw, the super-root token will delete itself and it's child root token, which will then proceed up the tree deleting all tokens as usual. If the parser is successful, the root token is orphaned by the super-root token, and is returned. Either this or a try/catch block around the whole function with some code to delete all members in the vector. Actually, the latter seems a better idea. However, this isn't the only problem (others are constructing objects in a scenario from a file, loading models, converting models), and I'm looking for a general way to avoid this issue. ========================================================== I also have a couple more generic questions on exceptions: 1. Is it good practice to end a try/catch block with an empty (catch all) catch, or should you let unknown exceptions halt the program? 2. Is it possible to throw across DLL or module lines? Eg, a DLL throws and the main program catches, or vice versa. 3. What happens if you throw from a secondary thread and it isn't caught within the thread's entry function. ============================ Thanks for all assistance.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by yacwroy
I'm quite new to Exception Handling. I resisted the idea for a long time because
My current ideas are:
1. Design the function so that all heap objects are pointed to by a function-local object that will destroy them (such as my heap-allocated token tree below having a function-local stack-allocated root).
2. Create some sort of pointer template class that deletes on destruction, and clear it before a natural function exit. (Seems like an ugly hack to me).
This already exists in C++: std::auto_ptr
By using these kinds of RAII objects you can usually avoid try/catch blocks altogether.

Quote:
I also have a couple more generic questions on exceptions:
1. Is it good practice to end a try/catch block with an empty (catch all) catch, or should you let unknown exceptions halt the program?
2. Is it possible to throw across DLL or module lines? Eg, a DLL throws and the main program catches, or vice versa.
3. What happens if you throw from a secondary thread and it isn't caught within the thread's entry function.
1) No. Doing that will silently throw away valid exceptions without handling them. If you can't handle an exception then you shouldn't be catching it.
(If no-one catches the exception, that means no-one knew how to handle it, so it's fair that the program crashed ;))

2) I'm not sure. I think it's undefined behaviour...
However, I've used a framework before that allowed me to throw an exception on a network client and catch it on the server -- So if it isn't possible, I'm sure someone's published a work-around.

3) AFAIK, the thread is terminated, which is bad (just like if the main thread fails to catch an exception).
The framework that I mentioned above also had a mechanism for catching exceptions in the thread-main function and posting them back to the main thread somehow. Don't ask me how this was implemented tho ;p

[Edited by - Hodgman on July 9, 2008 1:48:54 AM]

Share this post


Link to post
Share on other sites
Quote:

Quote:
Quote:
Original post by yacwroy
I'm quite new to Exception Handling. I resisted the idea for a long time because
My current ideas are:
1. Design the function so that all heap objects are pointed to by a function-local object that will destroy them (such as my heap-allocated token tree below having a function-local stack-allocated root).
2. Create some sort of pointer template class that deletes on destruction, and clear it before a natural function exit. (Seems like an ugly hack to me).


This already exists in C++: std::auto_ptr


Hmm, I wouldn't really recommend auto_ptr. Auto_ptr has weird copying, the act of copying an auto_ptr sets the original source of the copy to NULL. I would take a look at boost::shared_ptr instead (or if your compiler supports it already std::tr1::shared_ptr which is the standardized version based on the boost one) which are both complete reference counted smart pointers.

If you use auto_ptr you cannot store them in standard containers and expect them to work, shared_ptrs however can be stored in containers and work correctly.

Share this post


Link to post
Share on other sites
Quote:
Original post by Kazade
Hmm, I wouldn't really recommend auto_ptr. Auto_ptr has weird copying, the act of copying an auto_ptr sets the original source of the copy to NULL. I would take a look at boost::shared_ptr instead (or if your compiler supports it already std::tr1::shared_ptr which is the standardized version based on the boost one) which are both complete reference counted smart pointers.


You cannot recomment shared_ptr over auto_ptr in the general case, simply because the two have different purposes. The former is used to represent shared ownership semantics (which the latter cannot do), while the latter is used to represent exclusive ownership transferral (which the former cannot do).

For the purposes of cleaning up after yourself during stack unwinding (exception-friendly RAII at function scope), the best alternative is scoped_ptr which is designed to represent exactly that, although it's true that auto_ptr can be used as a cheap replacement when the boost libraries are not available.

Share this post


Link to post
Share on other sites
maybe i am just anal - but at least for personal code, i dont use catch handlers for doing resource management such as deleting allocated memory. instead everything gets treated as raii (memory, files, threads, network/db connections). this makes sense from the point of view of encapsulating functionality. catch handlers are then distinguished for their value in dealing with more high level state stuff/logging which is normally far away from the throw point.

Share this post


Link to post
Share on other sites
Thanks all for your replies. ++:).

Ok RAII sounds like a good thing.

'shared_ptr' sounds a little bulky for what I need, and I prefer the idea of unique ownership.

'scoped_ptr' would be the best, but it doesn't have much over 'auto_ptr' and it requires boost. This is just slightly more annoying since I plan to have plugins which would require the end user to install boost. So, is the minimal annoyance worth the minimal benefit.

I think I'll try using 'auto_ptr' for now. Ownership transfer via copy is actually kinda useful, returning the pointer prevents destruction etc.

I wonder, does using exceptions increase program efficiency? Less return-code checks, but features like RAII have overheads too.

Share this post


Link to post
Share on other sites
exceptions have a certain amount of over head, i've always been curoius as to exactly how much, but from what I understand they are certainly *not* more efficient.

Still they are incredibly useful, and in all but the most extreme performance cases, they create much more manageable code that's well worth the slight performance hit.

Share this post


Link to post
Share on other sites
Quote:
Original post by yacwroy
'scoped_ptr' would be the best, but it doesn't have much over 'auto_ptr' and it requires boost. This is just slightly more annoying since I plan to have plugins which would require the end user to install boost. So, is the minimal annoyance worth the minimal benefit.


The pointer library in Boost is a header-only library, as is most of Boost. This means no 'installation' as such is required. Just get the header files, point your compiler/IDE to the right spot, and include <boost/scoped_ptr.hpp>, and you're done. Other ridiculously helpful header-only libraries include Boost.Function, Boost.Bind, and Boost.LexicalCast.

Share this post


Link to post
Share on other sites
IIRC, exceptions have a large amount of overhead only WHEN they are thrown. But typical code execution doesn't throw.

If the code doesn't cause an error then (either using return codes or exceptions), it'd just be run some logical test, then one jump-if-error-bit-is-set or similar. And if the code throws, hopefully nobody cares too much about efficiency. But there is an additional test required if return codes are used (after the returning function exits).

The exception handling code may be far away so as to not clutter up your RAM pages (just my guess - that's how I'd do it), wheras the returned error handling code could be located anywhere. My guess is that the exception handling code would typically be far larger, making for larger files, but well out of the way and unlikely to end up causing performance reduction.

However that's mostly speculation.




Share this post


Link to post
Share on other sites
Ideally everything is done using RAII / smart-pointers, and you can throw an exception from anywhere that gets caught in what could be your only catch block in main, and upon entering the catch block, ALL memory allocated everything else on the stack has already been freed automagically.

In other words: To leverage the true power of exception handling you must first master RAII.

Share this post


Link to post
Share on other sites
Quote:
Original post by iMalc
Ideally everything is done using RAII / smart-pointers, and you can throw an exception from anywhere that gets caught in what could be your only catch block in main
That's not how exception handling works, you're not supposed to catch everything from the whole app in one place. Unless I just misunderstood what you meant to say.

Share this post


Link to post
Share on other sites
Quote:
Original post by yacwroy
I'm not really sure that I believe try/throw/catch is better than returning error codes, but I'm trying it out.
If you're not convinced, do more research before you start using them? Exceptions aren't so elegant in C++, but they are a fundamental part of Java/C# so it's beneficial to master them.

Of course in console game development, many people will forbid you using exceptions. Whether that's sensible depends on the platform/compiler... lots of "rules" in console development are based on problems that used to exist on old consoles.

Share this post


Link to post
Share on other sites
Quote:
Original post by yacwroy
I also have a couple more generic questions on exceptions:
1. Is it good practice to end a try/catch block with an empty (catch all) catch, or should you let unknown exceptions halt the program?

If you use an empty try catch block, what will you do? Run the whole program again? Quit with a completely unhelpful error message? It's often argued that the only place you should use catch(...) is when you need to do some housework before re-throwing the exception (using "throw;").

Because exceptions are caught polymorphically, the type of an exception carries a lot of meaning. By writing catch(...), you appear to be panicking because you don't know the cause of the exception. Catch only the errors you know you can handle. Any more may be too much, especially if you're calling virtual functions; you can't know what any of the custom exceptions they might throw are trying to tell you.

Quote:
Original post by yacwroy
2. Is it possible to throw across DLL or module lines? Eg, a DLL throws and the main program catches, or vice versa.


The standard doesn't define the concept of a DLL. It will depend on the compiler (and the flags you give it to create the library and application). I would advise against letting exceptions escape from a DLL.

Quote:
Original post by yacwroy
3. What happens if you throw from a secondary thread and it isn't caught within the thread's entry function.

The standard doesn't yet define the concept of a thread, so this is also entirely undefined. Check the documentation for your platform.

What I tend to do is create a "cage" object that's capable of calling a functor and catching, storing and re-throwing a number of different exception types. It's a rather long winded technique, but it's about the best that can be done without extra support from the language (which is coming soonish).

Quote:
Original post by d000hg
Quote:
Original post by yacwroy
I'm not really sure that I believe try/throw/catch is better than returning error codes, but I'm trying it out.
If you're not convinced, do more research before you start using them? Exceptions aren't so elegant in C++, but they are a fundamental part of Java/C# so it's beneficial to master them.


I'd actually say they're more elegant in C++, at least in so far as they don't require extra scaffolding for error handling if things are done "the C++ way". In Java, to write exception safe code, I have to put try/finallys everywhere and in C# I have to make everything IDisposable and add using(xyz) all over the place. By using RAII, none of this is needed.

But there are warts of course, as highlighted by the O.P.

Share this post


Link to post
Share on other sites
Quote:
Original post by the_edd
C# I have to make everything IDisposable and add using(xyz) all over the place. By using RAII, none of this is needed.


In fact, by using RAII, IDisposable and 'using' functionality is needed! That's in fact the entire point of RAII: resources are released in the destructor and the destructor is called when execution leaves the scope of a variable. The reason why you have to do this in C# but not in C++ is because, by default, all C++ objects are IDisposable, and all auto variables implement 'using' functionality, whereas you have to explicitly ask for it in C#.



Share this post


Link to post
Share on other sites
Quote:
Original post by d000hg
Quote:
Original post by iMalc
Ideally everything is done using RAII / smart-pointers, and you can throw an exception from anywhere that gets caught in what could be your only catch block in main
That's not how exception handling works, you're not supposed to catch everything from the whole app in one place. Unless I just misunderstood what you meant to say.
You did misinterpret slightly. I suggested that having only one catch block in main and catching it there is what one could do (and everything would be cleaned up). In practice one would tend not to do that.
However it is also considered good practice by many, that about the only catch(...) handler is in main and that all other handlers catch specific exception types.

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
Quote:
Original post by the_edd
C# I have to make everything IDisposable and add using(xyz) all over the place. By using RAII, none of this is needed.


In fact, by using RAII, IDisposable and 'using' functionality is needed! That's in fact the entire point of RAII: resources are released in the destructor and the destructor is called when execution leaves the scope of a variable. The reason why you have to do this in C# but not in C++ is because, by default, all C++ objects are IDisposable, and all auto variables implement 'using' functionality, whereas you have to explicitly ask for it in C#.


Well, in C++ there is also only the destructor. In C# there is both the IDisposable and finalization stuff to prepare.

Nevertheless, my phrasing was poor. RAII and IDisposable/using serve a similar purpose as you say, but the point I was really trying to make was that I prefer to have the roll-back code implicit as it creates less noise inside the function. It seems neater and more readable to me.

Share this post


Link to post
Share on other sites
Quote:
Original post by Wavesonics
exceptions have a certain amount of over head, i've always been curoius as to exactly how much, but from what I understand they are certainly *not* more efficient.


About 2% worst-case per function call under MSVC when not thrown, due to prolog/epilog and code size increase. That comes from some 4 or 5 instructions per function.

Quote:
IIRC, exceptions have a large amount of overhead only WHEN they are thrown. But typical code execution doesn't throw.


Is it "overhead", when they do what must be done to continue execution properly? Is it overhead to delete resources, rather than leave them in undefined state?

Throwing an exception and reclaiming the resources is exactly the same as needs to be done manually with return codes - given equivalent algorithm.

The overhead in this case is the difference between minimal implementation of such mechanism, and one provided by exceptions. This overhead is negligible by itself.

When handling errors manually, it's possible to use different algorithm and code structure, which does less work, but in a non-cosistent and specialized manner.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Is it "overhead", when they do what must be done to continue execution properly? Is it overhead to delete resources, rather than leave them in undefined state?.

That is not the overhead when throwing an exception.

The exception handling in most modern C++ runtimes involves crawling the stack looking for a suitable catch for the exception being thrown, and then actually copying the exception object, unwinding the stack,and invoking the catch-clause. The stuff after the "and then" is not unique overhead, since it has to be performed whether using an exception-based model or a status-return-and-check model of cleaning up. It's the first phase that's unique to the exception model: it requires the use of RTTI and other runtime cruft. It is actually significantly expensive compared to using return codes.

Throwing an exception is more expensive than the C-idiom of status-return-and-check for two reasons: first, the abovementioned extra overhead of the first-phase stack crawl, and second because the price of returning the status and checking it is removed from each function invocation and rolled into the throw operation. The status-return-and-check idiom factors the cost of stack unrolling into each and every function call whether an exceptional condition obtains or not. With exceptions, you pay for what you use.

So, although exceptions are expensive when thrown and the cost of throwing an exception is greater than the cost of returning status codes, exceptions are cheaper when not thrown because all the overhead is concentrated on the throw instead of factored across all non-exceptionsl uses.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement