开发者

Objection to exceptions

One of my friends raised this problem to me. I was stuck because I am not an adept at using exceptions. Keep in mind that we both work in an working environment where we use C++ but do error handling in C tradition. His problem was something like this:

Function A calls B which in turn calls C. An exception is thrown from C and the catch block for that exception is in A. What happens to resources acquired in B before the call to C? How do we clean those up? My answer was use RAII. But even when I said it, I knew it wasn't gonna work. We have huge code bases that have been written in the C mode. Nowhere in code have I seen auto poi开发者_开发技巧nters and such. Resources aren't necessarily wrapped up in classes. Even when they are, the destructors are left for the compiler in most cases. In short, everything is done manually.

The real problem is what to do to make the transition from C error handling to exceptions with the weight of a huge code base? The problem my friend asked is just one of the possible questions that can be raised when you have your feet grounded in C error handling and want to know how the migration from there to exceptions can occur.


There is a technique developed by Andrei Alexandrescu and Joshua Lehrer called Scope Guard which provides a technique for providing a "scope-exit" code as soon as you allocate an object, and opening the scope for it at that point.

The D programming language actually has it as standard.

boost libraries has an enhancement of this called Scope Exit. If your code is not exception-safe, as function B appears not to be, it will not handle properly if anything throws.

Destructors will run when unwinding the call-stack after an exception. That is why you must ensure they do not themselves throw any exceptions.


The real problem is what to do to make the transition from C error handling to exceptions with the weight of a huge code base?

No, the real problem is that, since you're programming in C++, you should have long since be done with this. Even if your own code does not throw exceptions, some code in the standard library and runtime system might throw (new, dynamic_cast) and, of course, any 3rd-party software you use might throw was well.

I agree that Scope Guard is probably your best bet to add exception-safety as an afterthought. However, don't fool yourself by thinking you don't need to do that as long as you don't throw exceptions yourself. You are using a language with exceptions, so you'd better start dealing with them as soon as possible.


You should migrate to RAII anyway because you can't make errors with it when you change the control flow, amongst other things. You need to upgrade. Exceptions mean that you can't half-upgrade any function that may have an exception in it.


C++ Does not support finally for managing last minute stuff in the end of the block.

What will happen is that when function C throws an exception, it returns from the function. Which means all the local variables will be destroyed.
Then the program will return to the code in B , it will check for a catch block, see that there is non, and jump back to function A. Again, all local variables in B will be freed.

You got to remember that you're working in C++.... You have to manage your objects. So if you have objects just wondering about freely in B (with pointers or whatever), then it's a bad code design, and they won't released.

If you know in B that there might be an exception in C , you can just put a catch there, and then throw back the exception caught.

The closest you can get to a finally block is using catch(...) and use it to free memory ... but it will only be entered if there was actually an exception so it's not identical to finally

try {

}
catch(...) {
  // free stuff affected by an exception here
}

If you want to work exactly like a finally block you can do:

try {
   //Do stuff
   goto finally
}
catch(...) {
  finally:
  // free stuff affected by an exception here
}

For all of you that have a problem with goto ... it still IS a command and this is a perfect example that no break or continue or some inline function can replace goto.

Some more about goto


Your friend is exactly right, and this is one of the reasons that Google does not use exceptions internally. See http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions (and open up the little arrow) for their explanation of the issue.


An excerpt from my abandoned homepage...

Without repeating in the detail all problems inherent to exception handling(1) I want to remark only the main idea: the complex part isn't where the exception is generated (throw) or where the exception is handled (catch) but in the methods of all classes the exception passes through almost asynchronously during the stack unwinding process.

Paying some attention on how methods are written is relatively easy avoid losing resources in case of exceptions (use of auto_ptr and similar classes) but things are much more complex when one takes in account the logical state of the object or other objects manipulated before the throw. In the most strict meaning of exception-safe a method should either succeed or being a no-op: in other words the logical state of the object called (and other involved objects) should be the same if an exception is thrown: no difference should be visible through the public interface of all involved classes.

...

(1) For an extensive description read the article "Exception Handling: A False Sense of Security" by Tom Cargill available online or the interesting collection of problems/solutions that that article generated on USENET available on the book "Exceptional C++" by Herb Sutter ISBN=0-201-61562-2.


Be glad you have exceptions. Consider what happens with error return codes in your example. Function C returns an error code. B, being naive as it is, swallows it. Caller A, which would be capable of handling it, isn't even informed about the error.

Your best bet is to go from C straight to C++0x. It offers a new feature, lambda's. These are anonymous functions, defined within other functions. You can put your C cleanup code in there, and have it called by a "scopeguard" object. Example (I'd skip the macro)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜