Related
I recently learned that in C++ there is a single "slot" for an active exception and this raise some questions for me.
The example that I know is something like:
void f()
{
MyClass c;
throw MyException;
}
When we get to the throw the function immediately returns and then the exception starts looking for its matching catch. But let's say that during the return of f, when the destructor of c is called, suppose it throws another exception of type MyException2, in this case from what I understood the program always crashes because there is only one "slot" for an active exception so when MyException2 occurs there is a problem.
I would like to know if there is a reason for this specific approach since most other languages have some sort of stack and give a message like During handling of this exception this other exception occurred, with clear stack traces for both exceptions.
Is there a reason for this problem not being approached? Things like a stack of active exceptions and if MyException2 occurs while MyException1 is active then it will just resolve and then return to the handling of MyException1?
I'm sorry if I may not be familiar with the proper terms, this is a purely education question from someone who wishes to know more about the "guts" of the language out of pure fascination.
What you’re thinking about in, say, Python looks like
try: risky()
except Error as e:
some_cleanup(e)
also_risky() # raises OtherError
if still_unhappy(): raise
more_handling()
The corresponding code in C++ behaves the same way and does not automatically terminate:
try {risky();}
catch(const Error &e) {
some_cleanup(e);
also_risky(); // throws OtherError
if(still_unhappy()) throw;
more_handling();
}
The reason is that the original exception might or might not have been handled successfully (is more_handling necessary, or is it just part of retrying?). C++ makes the optimistic statement that it was, whereas by default Python 3 makes the pessimistic statement that the new exception is fallout from the old. C++ could of course provide a syntax to indicate the other choice, like Python does (raise new from None), but such an “associated exception” would be more difficult still to deal with in a static-typing context than exceptions already are, and one of the main motivations is absent since C++ expects the program to provide whatever backtrace-reporting mechanisms are desired.
The parallel is stronger if you consider destructors in both languages (especially in CPython where many of them are reliably invoked during stack unwinding): here, Python simply prints to standard error if a destructor throws, because to do otherwise would greatly complicate its internal resource management. C++ is actually more permissive in that a destructor can throw so long as it was not invoked directly by the unwinding mechanism; this is dangerous, but is occasionally used by experts to good effect. On the other hand, C++ most certainly does not consider writing to standard error a sufficient option for handling an exception, so if its resource management encounters such a problem, it’s all over.
I saw a function that uses throw to return something.
I've made some research before asking this, here, about throw and I did not find very much.
If someone could explain me when to use throw to return something and when to use the normal return statement, that would be awesome.
Using throw to return a result can be useful in a deeply nested recursion. Instead of backing out of each call level you get right back up to the top level call for this goal. I think in most other situations it's both too inefficient (because exceptions are optimized for failure handling) and too unconventional to even consider.
return is the easiest and in many situations also the most efficient way to return a computed value, a reference to a class member, a state information (e.g. an enum value), a trivial success/failure information, and, and, and. So this is the most common and usual way to evaluate function values.
throw on the other hand (like Alf already answered) deals with exception's - which, as the name already pretty clearly states :) an exception of the normal control flow: "Something very important (often something bad) happened, which requires an instant handling, I can not continue my usual processing flow and have to throw an exception immediately.". The reason can sometimes be a catastrophic and unrecoverable state in the program, but by no means always. For example, a socket losing its connection is pretty common and not a catastrophy - but can be a reason for throwing an exception nevertheless, because the socket code may be not able to continue computing.
Exceptions are usually a bit more difficult to handle - and to integrate into your program design - than usual return values, and they are - as others already stated - less efficient than simple return values, but they have a lot of benefits.
They can be caught from anywhere in the stack of calling functions
Let's say you write a game, with a game engine (e.g. Ogre), and this engine uses a direct X interface. Now something deep, deep in the DirectX interface happens, which hinders the engine from working correctly.
The error handling of this function (may be 8-10 calls deep in the call stack), which can not work properly anymore, would be nearly impossible if it would be done with normal return values - both for the engine programmers and for the game programmer. So in that case, without exceptions, the method of choice would be a non-standardized error handler - pretty similar to an exception, but not with the powerful possibilities of exceptions. Here's a practical example on how to handle this error with an exception (please ignore the real purpose of the functions, it's just to show the principle:
try
{
mHOQList[mCurrentFrame]->endOcclusionQuery();
} catch( Ogre::Exception& e )
{
if( e.getNumber() == Exception::ERR_RENDERINGAPI_ERROR
&& stdEx::string(e.getDescription()).beginsWith( "End occlusion called" ))
{
// a device lost occurred during our occlusion query. Simply ignore it.
return true;
}
else
throw;
}
We are doing an occlusion query here, which we know can not continue when a "device lost" event happens during it's operation. So we place it in a try/catch clause. When everything works out good in the endOcclusionQuery(), the catch() is never called, and everything is fine.
If an exception is thrown, we first check if we can handle it. We check the number of the exception and its description. If these informations have a specific value, we know that it's a benign error, we can safely ignore it and carry on the next frame. If we don't know how to handle it, we simply throw;, which leaves the handling of the exceplion to a catch() lower in the call hierarchy, which brings me to the next points:
They can be evaluated selectively.
The example above will catch and handle exceptions of the type Ogre::Exception, but nothing else. A std::exception or another exception type is not caught. Let's say for example the endOcclusionQuery() calls a callback in our own code, which then also gets into an exceptional state and throws. We would let this exception pass by and leave it to the lower (or higher) levels of the call hierarchy to handle it.
They can be rethrown.
In the example, we use throw; to re-throw and pass the handling to lower levels in the call hierarchy.
They can be stored and even re-thrown in a separate thread
Imagine a library with hundreds of worker threads, and a manager thread which coordinates these threads. Since exceptions are bound to a single thread, so the manager thread can never catch exceptions from the worker threads. But the worker threads can catch their own exceptions and either handle them if possible or store them, pass them to the manager thread, where it can be rethrown and be handled by the manager thread.
They can be thrown in Constructors
Constructors have no return value, and so it is impossible to check its success with return values. State members are possible, but awkward - they tend to be overlooked. So the preferrable way to deal with errors in a constructor is to throw (of course as a documented behavior). See also Throwing exceptions from constructors
They are standardized
Well when it comes to this, the example above is not the best. Ogre is a pretty old engine, long before C++11, and so the exception classes are proprietary. You can throw anything - from a char to a class LiverSausage. But today this shouldn't be done anymore - std::exception is the class of choice. It contains everything which is needed for a simple exception, and can be inherited for more sophisticated exceptions. It is commonly used and inherited in the STL, and there are helpers classes and functions like std::exception_ptr, std::current_exception() etc.
They can be used as a safe bailout for an unrecoverable program error. At the end, something ugly like this unfortunately can happen in the best programs. You can throw this fatal exception anywhere in your program, and you can catch this exception at a single place, where you can log the error, evaluate where it came from, perhaps even write a dump - so you have at least a clue what could have happened, which is at least less worse than simply crashing ;)
throw isn't used to return any value , it is used to throw exceptions i.e. if you think that a certain condition in the program is going to cause a runtime error or malfunction then you throw a exception which helps to avoid and deal with such runtime errors. Return is used to return from a function and a value to the calling function.
While this might sound bad, I actually let performance be a strong factor in guiding this decision. Most modern optimizers implement what's called zero-cost exception-handling, which ultimately translates to something like, "branch-free normal execution paths, but expensive exceptional paths".
It makes throwing quite expensive in exchange for making your normal execution paths really cheap. I don't have precise numbers for the costs but it's probably relatively extremely expensive if you're using a try/catch block just to test if a key exists in a data structure, e.g.
The other guiding force that I've found useful is the idea of an external exception outside of the programmer's control. Examples of that might be a client failing to connect to a server which should be up after repeated attempts, encountering a corrupt file, failing to allocate memory, things of this sort.
I did get into a debate one time with a colleague about whether a user jamming an abort button on a progress bar qualifies as an exceptional circumstance. I really thought it did because the operation is normally supposed to succeed, and a user aborting is a truly exceptional circumstance outside of the developer's control. On top of that it really simplified the code quite a bit over trying to propagate an abort status down the entire call stack.
In these cases where people might not agree about what is and isn't exceptional control flow, I use performance as like the deciding factor. Here throwing to abort an expensive operation isn't a big performance overhead, since it's not like we're throwing a million times in a critical loop. We're just throwing one time to abort a very expensive operation, and there the overhead of throwing becomes quite trivialized. So that's what I mean when I talk about performance being a deciding factor.
People have argued pretty strongly against throwing exceptions from destructors. Take this answer as an example. I wonder whether std::uncaught_exception() can be used to portably detect whether we are in the process of unwinding the stack due to some other exception.
I find myself deliberately throwing exceptions in destructors. To mention two possible use cases:
Some resource cleanup which involves flushing buffers, so that failure likely signifies truncated output.
Destruction of an object holding a std::exception_ptr which might contain an exception encountered in a different thread.
Simply ignoring these exceptional situations feels plain wrong. And chances are that by throwing an exception some exception handler might be able to provide more useful context information than if the destructor itself were writing to std::cerr. Furthermore, throwing exceptions for all failed assertions is an important part of my unit testing approach. An error message followed by an ignored error condition wouldn't work in that case.
So my question is, is it OK to throw exceptions except when another exception is being processed, or is there a reason not to do that?
To put this in code:
Foo::~Foo() {
bool success = trySomeCleanupOperation();
if (!success) {
if (std::uncaught_exception())
std::cerr << "Error in destructor: " << errorCode << std::endl;
else
throw FooOperationFailed("Error in destructor", errorCode);
}
}
As far as I can tell, this should be safe and in many cases better than not throwing an exception at all. But I'd like to verify that.
Herb Sutter has written on the subject: http://www.gotw.ca/gotw/047.htm
His conclusion is to never throw from a destructor, always report the error using the mechanism that you would use in the case where you can't throw.
The two reasons are:
it doesn't always work. Sometimes uncaught_exception returns true and yet it is safe to throw.
it's bad design to have the same error reported in two different ways, both of which the user will have to account for if they want to know about the error.
Note that for any given reusable piece of code, there is no way to know for sure that it will never be called during stack unwinding. Whatever your code does, you can't be certain that some user of it won't want to call it from a destructor with a try/catch in place to handle its exceptions. Therefore, you can't rely on uncaught_exception always returning true if it's safe to throw, except maybe by documenting the function, "must not be called from destructors". If you resorted to that then all callers would also have to document their functions, "must not be called from destructors" and you have an even more annoying restriction.
Aside from anything else, the nothrow guarantee is valuable to users - it helps them write exception-safe code if they know that a particular thing that they do won't throw.
One way out is to give your class a member function close that calls trySomeCleanupOperation and throws if it fails. Then the destructor calls trySomeCleanupOperation and logs or suppresses the error, but doesn't throw. Then users can call close if they want to know whether their operation succeeds or not, and just let the destructor handle it if they don't care (including the case where the destructor is called as part of stack unwinding, because an exception was thrown before getting to the user's call to close). "Aha!", you say, "but that defeats the purpose of RAII because the user has to remember to call close!". Yes, a bit, but what's in question isn't whether RAII can do everything you want. It can't. What's in question is whether it consistently does less than you'd like (you'd like it to throw an exception if trySomeCleanupOperator fails), or does less surprisingly when used during stack unwinding.
Furthermore, throwing exceptions for all failed assertions is an important part of my unit testing approach
That's probably a mistake - your unit testing framework should be capable of treating a terminate() as a test failure. Suppose that an assert fails during stack unwinding - surely you want to record that, but you can't do so by throwing an exception, so you've painted yourself into a corner. If your assertions terminate then you can detect them as terminations.
Unfortunately if you terminate then you can't run the rest of the tests (at least, not in that process). But if an assertion fails then generally speaking your program is in an unknown and potentially unsafe state. So once you've failed an assertion you can't rely on doing anything else in that process anyway. You could consider designing your test framework to use more than one process, or just accept that a sufficiently severe test failure will prevent the rest of the tests running. Externally to the test framework, you can consider that your test run has three possible outcomes "all passed, something failed, tests crashed". If the test run fails to complete then you don't treat it as a pass.
This is what standard says about dtors and exceptions:
15.2
(...)
The process of calling destructors for automatic objects constructed on the path from a try block to the
point where an exception is thrown is called “stack unwinding.” If a destructor called during stack unwinding
exits with an exception, std::terminate is called (15.5.1). [ Note: So destructors should generally catch
exceptions and not let them propagate out of the destructor. —end note ]
Since you asked very ambiguous question, the answer depends:
Can you throw an exception in dtor such that application won't crash? Yes, you can (in terms: it will compile and sometimes it will run correctly).
Should you throw an exception in dtor? No, you should not, because it may (and usually it will) cause you problems.
I'd say, that a need for throwing an exception from dtor is a sign of bad design. It seems, that you're doing something in your destructor, that should be done elsewhere.
Each time I have seen the catch all statement:
try
{
// some code
}
catch (...)
{
}
it has always been an abuse.
The arguments against using cache all clauses are obvious. It will catch anything including OS generated exceptions such as access violations.
Since the exception handler can't know what it's dealing with, in most cases the exceptions will manifest as obscure log messages or some incoherent message box.
So catch(...) seems inherently evil.
But it is still implemented in C++ and other languages (Java, C#) implements similar mechanisms. So is there some cases when its usage is justified?
(1) It's not true that the statement will catch OS exceptions. Your use of the term "Access Violation" betrays a Windows background; it was true for older MSVC++ versions.
(2) Regardsless, the catch-all behavior is useful for threads with specific purposes. Catching the failure allows the thread to report it failed. Without it, the other parts of the program need to deal with the possibility of a thread just disappearing. It also allows you to log which thread failed, and the arguments used to start the thread.
The case where it's justified in general is when you log the exception (or do something similar) or do some cleanup, and then immediately rethrow.
In C++ in particular, logging in a catch(...) block is pretty pointless since you don't have any way to obtain the exception, and cleanup is pointless because you should be using RAII for that. Using it in destructors seems to be about the only legitimate case.
the arguments against using cache all clauses are obvious , it will catch anything including OS generated exceptions such as access violation. since the exception handler can't know what its dealing with, in most cases the exceptions will manifest as obscure log message or some incoherent message box.
And if those same exceptions aren't caught you get... an incoherent message box.
catch(...) lets me at least present my own message box (and invoke custom logging, save a crash dump, etc.).
I think there are also reasonable uses of catch(...) in destructors. Destructors can't throw--well, I mean, they can throw, but if a destructor throws during stack unwinding due to an in-progress exception the program terminates, so they should not ever allow exceptions to escape. It is in general better to allow the first exception to continue to be unwound than to terminate the program.
Another situation is in a worker thread that can run arbitrary functions; generally you don't want an unceremonious crash if the task throws an exception. A catch(...) in the worker thread provides the opportunity for semi-orderly clean-up and shutdown.
In addition to what other posters have already said, I'd like to mention one nice point from the C++ Standard:
If no matching handler is found in a
program, the function std::terminate()
is called; whether or not the stack is
unwound before this call to
std::terminate() is
implementation-defined.
(15.3/9)
This means that main() and every thread function must be wrapped in a catch-all handler; otherwise, one can't even be sure that destructors for automatic objects will be called if an uncaught exception is thrown.
try {...} catch (...) is needed around body of callback function which is called from code
that doesn't understand C++ exceptions (usually C library).
Otherwise, if some C++ library you use throws an exception that doesn't derive from
std::exception, it will probably cause calling code to crash or corrupt its internal state.
Instead you should catch this exception and either finish program immediately or
return some error code (meaning "we are doomed and I don't know why", but it's still better
then letting C++ exception to pass through).
Around thread procedure. Mostly because of the same reason as 1.
And because otherwise thread failure would pass unnoticed.
catch(...) has been useful for me in two circumstances, both of which are unjustified (I can't even remember the second)
The first is my overall application safety. While throwing exceptions that don't derive from std::exception is a No-No, I have one just in case in my main() function:
int execute(void); // real program lies here
int main(void)
{
try
{
return execute();
}
catch(const std::exception& e)
{
// or similar
std::cerr << "Unhandled exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
catch(...)
{
std::cerr << "Unknown exception!" << std::endl;
return EXIT_FAILURE;
}
}
Now, it's only there "just in case", and it's not really justified. There should be no reason to ever enter that catch clause, as that would mean somebody has done a Bad Thing. Observe how useless the statement really is; "Something bad happened, no clue what!" It's only a step above just crashing in the first place.
The second use might be in destructors or some other function that needs to do manual management before letting the exception propagate. That's not really a justification either, as things should clean themselves up safely with RAII. But I may have used it once or twice for some reason I can't recall, and I can't see a reason to ever do so again.
catch (...) allows you to write code in which you can legitimately claim a guarantee that your code will not crash even when you are not in long term complete control of the submodules your code depends on. Your claim is tantamount to claiming that this semantic cannot be used except as a means of abuse. Maybe so, but military specifications may differ from you on this issue.
catch(...) is necessary in the absence of the finally clause as found in other languages:
try {
...
} catch(...) {
cleanup...
throw;
}
The alternative - making stack objects to 'own' everything - is often much more code and less readable and maintainable. The platform API is often C, and does not come with it conveniently bundled.
It is also useful around plugin code that you do not control or simply do not trust from a stability perspective. It won't stop them crashing, but it might keep things a little saner.
Finally, there are times when you really do not care about the outcome of something.
The more we use RAII in C++, the more we find ourselves with destructors that do non-trivial deallocation. Now, deallocation (finalization, however you want to call it) can fail, in which case exceptions are really the only way to let anybody upstairs know of our deallocation problem. But then again, throwing-destructors are a bad idea because of the possibility of exceptions being thrown during stack unwinding. std::uncaught_exception() lets you know when that happens, but not much more, so aside from letting you log a message before termination there's not much you can do, unless you're willing to leave your program in an undefined state, where some stuff is deallocated/finalized and some not.
One approach is to have no-throw destructors. But in many cases that just hides a real error. Our destructor might, for example, be closing some RAII-managed DB connections as a result of some exception being thrown, and those DB connections might fail to close. This doesn't necessarily mean we're ok with the program terminating at this point. On the other hand, logging and tracing these errors isn't really a solution for every case; otherwise we would have had no need for exceptions to begin with.
With no-throw destructors we also find ourselves having to create "reset()" functions that are supposed to be called before destruction - but that just defeats the whole purpose of RAII.
Another approach is just to let the program terminate, as it's the most predictable thing you can do.
Some people suggest chaining exceptions, so that more than one error can be handled at a time. But I honestly never actually seen that done in C++ and I've no idea how to implement such a thing.
So it's either RAII or exceptions. Isn't it? I'm leaning toward no-throw destructors; mainly because it keeps things simple(r). But I really hope there's a better solution, because, as I said, the more we use RAII, the more we find ourselves using dtors that do non-trivial things.
Appendix
I'm adding links to interesting on-topic articles and discussions I've found:
Throwing Destructors
StackOverflow discussion on the problems with SEH
StackOverflow discussion on throwing-destructors (thanks, Martin York)
Joel on Exceptions
SEH Considered Harmful
CLR Exception Handling which also touches on exception chaining
Herb Sutter on std::uncaught_exception and why it's not as useful as you think
Historical discussion on the matter with interesting participants (long!)
Stroustrup explaining RAII
Andrei Alexandrescu's Scope Guard
You SHOULD NOT throw an exception out of a destructor.
Note: Updated to refeclt changes in the standard:
In C++03
If an exception is already propagating then the application will terminate.
In C++11
If the destructor is noexcept (the default) then the application will terminate.
The Following is based on C++11
If an exception escapes a noexcept function it is implementation defined if the stack is even unwound.
The Following is based on C++03
By terminate I mean stop immediately. Stack unwinding stops. No more destructors are called. All bad stuff. See the discussion here.
throwing exceptions out of a destructor
I don't follow (as in disagree with) your logic that this causes the destructor to get more complicated.
With the correct usage of smart pointers this actually makes the destructor simpler as everything now becomes automatic. Each class tides up its own little piece of the puzzle. No brain surgery or rocket science here. Another Big win for RAII.
As for the possibility of std::uncaught_exception() I point you at Herb Sutters article about why it does not work
From the original question:
Now, deallocation (finalization,
however you want to call it) can fail,
in which case exceptions are really
the only way to let anybody upstairs
know of our deallocation problem
Failure to cleanup a resource either indicates:
Programmer error, in which case, you should log the failure, followed by notifying the user or terminating the application, depending on application scenario. For example, freeing an allocation that has already been freed.
Allocator bug or design flaw. Consult the documentation. Chances are the error is probably there to help diagnose programmer errors. See item 1 above.
Otherwise unrecoverable adverse condition that can be continued.
For example, the C++ free store has a no-fail operator delete. Other APIs (such as Win32) provide error codes, but will only fail due to programmer error or hardware fault, with errors indicating conditions like heap corruption, or double free, etc.
As for unrecoverable adverse conditions, take the DB connection. If closing the connection failed because the connection was dropped -- cool, you're done. Don't throw! A dropped connection (should) result in a closed connection, so there's no need to do anything else. If anything, log a trace message to help diagnose usage issues. Example:
class DBCon{
public:
DBCon() {
handle = fooOpenDBConnection();
}
~DBCon() {
int err = fooCloseDBConnection();
if(err){
if(err == E_fooConnectionDropped){
// do nothing. must have timed out
} else if(fooIsCriticalError(err)){
// critical errors aren't recoverable. log, save
// restart information, and die
std::clog << "critical DB error: " << err << "\n";
save_recovery_information();
std::terminate();
} else {
// log, in case we need to gather this info in the future,
// but continue normally.
std::clog << "non-critical DB error: " << err << "\n";
}
}
// done!
}
};
None of these conditions justify attempting a second kind of unwind. Either the program can continue normally (including exception unwind, if unwind is in progress), or it dies here and now.
Edit-Add
If you really want to be able to keep some sort of link to those DB connections that can't close -- perhaps they failed to close due to intermittent conditions, and you'd like to retry later -- then you can always defer cleanup:
vector<DBHandle> to_be_closed_later; // startup reserves space
DBCon::~DBCon(){
int err = fooCloseDBConnection();
if(err){
..
else if( fooIsRetryableError(err) ){
try{
to_be_closed.push_back(handle);
} catch (const bad_alloc&){
std::clog << "could not close connection, err " << err << "\n"
}
}
}
}
Very not pretty, but it might get the job done for you.
You're looking at two things:
RAII, which guarantees that resources are cleaned up when scope is exited.
Completing an operation and finding out whether it succeeded or not.
RAII promises that it will complete the operation (free memory, close the file having attempted to flush it, end a transaction having attempted to commit it). But because it happens automatically, without the programmer having to do anything, it doesn't tell the programmer whether those operations it "attempted" succeeded or not.
Exceptions are one way to report that something failed, but as you say, there's a limitation of the C++ language that means they aren't suitable to do that from a destructor[*]. Return values are another way, but it's even more obvious that destructors can't use those either.
So, if you want to know whether your data was written to disk, you can't use RAII for that. It does not "defeat the whole purpose of RAII", since RAII will still try to write it, and it will still release the resources associated with the file handle (DB transaction, whatever). It does limit what RAII can do -- it won't tell you whether the data was written or not, so for that you need a close() function that can return a value and/or throw an exception.
[*] It's quite a natural limitation too, present in other languages. If you think RAII destructors should throw exceptions to say "something has gone wrong!", then something has to happen when there's already an exception in flight, that is "something else has gone wrong even before that!". Languages that I know that use exceptions don't permit two exceptions in flight at once - the language and syntax simply don't allow for it. If RAII is to do what you want, then exceptions themselves need to be redefined so that it makes sense for one thread to have more than one thing going wrong at a time, and for two exceptions to propagate outward and two handlers to be called, one to handle each.
Other languages allow the second exception to obscure the first, for example if a finally block throws in Java. C++ pretty much says that the second one must be suppressed, otherwise terminate is called (suppressing both, in a sense). In neither case are the higher stack levels informed of both faults. What is a bit unfortunate is that in C++ you can't reliably tell whether one more exception is one too many (uncaught_exception doesn't tell you that, it tells you something different), so you can't even throw in the case where there isn't already an exception in flight. But even if you could do it in that case, you'd still be stuffed in the case where one more is one too many.
It reminds me a question from a colleague when I explained him the exception/RAII concepts: "Hey, what exception can I throw if the computer's switched off?"
Anyway, I agree with Martin York's answer RAII vs. exceptions
What's the deal with Exceptions and Destructors?
A lot of C++ features depend on non-throwing destructors.
In fact, the whole concept of RAII and its cooperation with code branching (returns, throws, etc.) is based on the fact deallocation won't fail. In the same way some functions are not supposed to fail (like std::swap) when you want to offer high exception guarantees to your objects.
Not that it doesn't mean you can't throw exceptions through destructors. Just that the language won't even try to support this behaviour.
What would happen if it was authorized?
Just for the fun, I tried to imagine it...
In the case your destructor fails to free your resource, what will you do? Your object is probably half destructed, what would you do from an "outside" catch with that info? Try again? (if yes, then why not trying again from within the destructor?...)
That is, if you could access your half-destructed object it anyway: What if your object is on the stack (which is the basic way RAII works)? How can you access an object outside its scope?
Sending the resource inside the exception?
Your only hope would be to send the "handle" of the resource inside the exception and hoping code in the catch, well... try again to deallocate it (see above)?
Now, imagine something funny:
void doSomething()
{
try
{
MyResource A, B, C, D, E ;
// do something with A, B, C, D and E
// Now we quit the scope...
// destruction of E, then D, then C, then B and then A
}
catch(const MyResourceException & e)
{
// Do something with the exception...
}
}
Now, let's imagine for some reason the destructor of D fails to deallocate the resource. You coded it to send an exception, that will be caught by the catch. Everything goes well: You can handle the failure the way you want (how you will in a constructive way still eludes me, but then, it is not the problem now).
But...
Sending the MULTIPLE resources inside the MULTIPLE exceptions?
Now, if ~D can fail, then ~C can, too. as well as ~B and ~A.
With this simple example, you have 4 destructors which failed at the "same moment" (quitting the scope). What you need is not not a catch with one exception, but a catch with an array of exceptions (let's hope the code generated for this does not... er... throw).
catch(const std::vector<MyResourceException> & e)
{
// Do something with the vector of exceptions...
// Let's hope if was not caused by an out-of-memory problem
}
Let's get retarted (I like this music...): Each exception thrown is a different one (because the cause is different: Remember that in C++, exceptions need not derive from std::exception). Now, you need to simultaneously handle four exceptions. How could you write catch clauses handling the four exceptions by their types, and by the order they were thrown?
And what if you have multiple exceptions of the same type, thrown by multiple failed deallocation? And what if when allocating the memory of the exception arrays of arrays, your program goes out of memory and, er... throw an out of memory exception?
Are you sure you want to spend time on this kind of problem instead of spending it figuring why the deallocation failed or how to react to it in another way?
Apprently, the C++ designers did not see a viable solution, and just cut their losses there.
The problem is not RAII vs Exceptions...
No, the problem is that sometimes, things can fail so much that nothing can be done.
RAII works well with Exceptions, as long as some conditions are met. Among them: The destructors won't throw. What you are seeing as an opposition is just a corner case of a single pattern combining two "names": Exception and RAII
In the case a problem happens in the destructor, we must accept defeat, and salvage what can be salvaged: "The DB connection failed to be deallocated? Sorry. Let's at least avoid this memory leak and close this File."
While the exception pattern is (supposed to be) the main error handling in C++, it is not the only one. You should handle exceptional (pun intended) cases when C++ exceptions are not a solution, by using other error/log mechanisms.
Because you just met a wall in the language, a wall no other language that I know of or heard of went through correctly without bringing down the house (C# attempt was worthy one, while Java's one is still a joke that hurts me on the side... I won't even speak about scripting languages who will fail on the same problem in the same silent way).
But in the end, no matter how much code you'll write, you won't be protected by the user switching the computer off.
The best you can do, you already wrote it. My own preference goes with a throwing finalize method, a non-throwing destructor cleaning resources not finalized manually, and the log/messagebox (if possible) to alert about the failure in the destructor.
Perhaps you're not putting up the right duel. Instead of "RAII vs. Exception", it should be "Trying to freeing resources vs. Resources that absolutely don't want to be freed, even when threatened by destruction"
:-)
One thing I would ask is, ignoring the question of termination and so on, what do you think an appropriate response is if your program can't close its DB connection, either due to normal destruction or exceptional destruction.
You seem to rule out "merely logging" and are disinclined to terminate, so what do you think is the best thing to do?
I think if we had an answer to that question then we would have a better idea of how to proceed.
No strategy seems particularly obvious to me; apart from anything else, I don't really know what it means for closing a database connection to throw. What is the state of the connection if close() throws? Is it closed, still open, or indeterminate? And if it's indeterminate, is there any way for the program to revert to a known state?
A destructor failing means that there was no way to undo the creation of an object; the only way to return the program to a known (safe) state is to tear down the entire process and start over.
What are the reasons why your destruction might fail? Why not look to handling those before actually destructing?
For example, closing a database connection may be because:
Transaction in progress. (Check std::uncaught_exception() - if true, rollback, else commit - these are the most likely desired actions unless you have a policy that says otherwise, before actually closing the connection.)
Connection is dropped. (Detect and ignore. The server will rollback automatically.)
Other DB error. (Log it so we can investigate and possibly handle appropriately in the future. Which may be to detect and ignore. In the meantime, try rollback and disconnect again and ignore all errors.)
If I understand RAII properly (which I might not), the whole point is its scope. So it's not like you WANT transactions lasting longer than the object anyway. It seems reasonable to me, then, that you want to ensure closure as best you can. RAII doesn't make this unique - even without objects at all (say in C), you still would try to catch all error conditions and deal with them as best as you can (which is sometimes to ignore them). All RAII does is force you to put all that code in a single place, no matter how many functions are using that resource type.
You can tell whether there is currently an exception in flight (e.g. we are between the throw and catch block performing stack unwinding, perhaps copying exception objects, or similar) by checking
bool std::uncaught_exception()
If it returns true, throwing at this point will terminate the program, If not, it's safe to throw (or at least as safe as it ever is). This is discussed in Section 15.2 and 15.5.3 of ISO 14882 (C++ standard).
This doesn't answer the question of what to do when you hit an error while cleaning up an exception, but there really aren't any good answers to that. But it does let you distinguish between normal exit and exceptional exit if you wait to do something different (like log&ignore it) in the latter case, rather than simply panicing.
If one really needs to deal with some errors during the finalization process, it should not be done within the destructor. Instead, a separate function that returns an error code or may throw should be used instead. To reuse the code, you can call this function inside the destructor, but you must not allow the exception to leak out.
As some people mentioned, it is not really resource deallocation, but something like resource commit during exit. As other people mentioned, what can you do if saving fails during a forced power-off? There are probably no all-satisfying answers, but I would suggest one of the following approaches:
Just allow the failure and loss to happen
Save the unsaved part to somewhere else and allow the recovery to happen later (see the other approach if this does not work either)
If you do not like either of this approaches, make your user explicitly save. Tell them not to rely on the auto-save during a power-off.