AS I understand that we can not throw exceptions from dtor, and the reason is said like: "if an exception was thrown inside 'stack unwinding', then there is no clear way to handle 'nested unwinding', thus 'throwing from dtor' is prohibited.".
What makes me confuse is that, to obey above rule, we write codes like this:
#include <stdexcept>
#include <string>
#include <stdio.h>
void some_cleanup_stuff_that_may_throw();
class Bar
{
public:
virtual ~Bar(){
try {
some_cleanup_stuff_that_may_throw();
}
catch (std::exception& e){
fprintf(stderr, "Exception: %s from %s\n", e.what(), __FUNCTION__);
}
}
};
//{{ implementation of 'some_cleanup_stuff_that_may_throw'
void level1();
void level2();
void some_cleanup_stuff_that_may_throw()
{
level1();
}
void level1()
{
level2();
}
void level2()
{
throw std::runtime_error("Yes thrown when stack unwinding");
}
//}} implementation of 'some_cleanup_stuff_that_may_throw'
void Foo1()
{
Bar b1;
throw std::runtime_error("Let's start stack unwinding");
}
int main()
{
try {
Foo1();
}
catch (std::exception& e){
fprintf(stderr, "Exception: %s from %s\n", e.what(), __FUNCTION__);
}
return 0;
}
So in some circumstance, 'nested stack unwinding' is possible.
My question is: when 'nested stack unwinding' is 'OK' ?
CodePudding user response:
Throwing an exception while stack unwinding is in progress is fine. But throwing an exception from a destructor of an object that is being unwound from the stack or from any other function invoked by the exception handling mechanism (e.g. a catch parameter constructor) causes a call to std::terminate
.
Throwing from a function here is supposed to mean that the thrown exception is not caught inside its body (or function try-catch block) and actually escapes the function.
In your example, no exception is leaving the destructor. So there is no problem.
It is allowed to have nested exceptions being handled or being in the process of stack unwinding. Once the nested exception is caught, handled and the destructor finishes, the original stack unwinding can continue calling the next destructor moving up the stack again. There is even API for this. For example you can use std::uncaught_exceptions()
which gives you the number of currently uncaught exceptions.
The implementation of the C runtime just has to make sure to keep track of all of the exception objects currently alive.
(I assume that you are not really interested in the exact implementation details of the unwind implementations. If you are, then please clarify this in the question.)