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

Started by
16 comments, last by Bregma 15 years, 9 months ago
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.

Advertisement
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.

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.
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#.



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.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
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.
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.
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.

Stephen M. Webb
Professional Free Software Developer

This topic is closed to new replies.

Advertisement