Detecting exception unwinding

cy via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Thu Feb 4 23:31:24 PST 2016


On Wednesday, 3 February 2016 at 11:09:00 UTC, Ola Fosheim 
Grøstad wrote:
> Is there some reliable way to detect that a destructor is 
> called because of exception unwinding?
>
> I basically want to change behaviour within a destructor based 
> on whether the destructor is called as a result of a regular or 
> an exceptional situation.
>
> E.g. commit changes to a database on regular destruction, or 
> inhibit logging during exception unwinding.

I think you might be talking about two very different concepts 
here. Unwinding only happens within the context of a certain 
scope. That's where it gets the backtrace from. If you construct 
an object, then save it somewhere globally, then return from the 
function, then go back into the event loop, then whatever... you 
no longer have your original scope. There can be no exceptional 
situation, nor can there be "regular destruction" because the 
scope has already unwound. Saving your object globally keeps it 
from being destructed, and you might use reference counting in 
that case I guess, but ultimately, when an exception occurs, your 
object will have nothing to do with it at all.

That might be your situation, in which case you simply do this:

bool fail = false;
...
class Foo {
...
   ~this() {
     if(!fail) writeln(shakespeare);
     ...
   }
...
}

int main() {
   scope(failure) fail = true;
   ...
}

When your program exits due to an uncaught exception, it can't 
unwind higher than main, so "scope(failure)" for main will apply 
to all uncaught exceptions that kill the program.  Any globally 
stored variables destructed after that will see fail as being 
true.

The other situation is easier, and probably what you're trying to 
do, so sorry for wasting your time. If you have a local variable 
in a local scope, then when you leave that scope normally, the 
variable will be destroyed, as well as when you fail out of it. 
You want to find out whether you are leaving that scope normally 
in the destructor, and it's not anything about whether the 
program is dying or not, but instead it's making sure your local 
objects with global state clean up before they die.

If that's what you're doing then you do this:

void some_routine() {
   ...
   Foo foo;
   scope(failure) foo.fail = true;
   ...proceed normally...
}

When some_routine exits normally, foo has not set fail, and it 
will be destroyed knowing that. When some_routine errors out, 
"scope(failure)" will set the fail on foo, and then when foo is 
destroyed it can do so quietly.

But again, two different situations. If you try to do this:

Foo foo;
void some_routine() {
   scope(failure) foo.fail = true;
   ...no exceptions...
}
void failure_routine() {
   ...
   throw new SomeException();
   ...
}

int main() {
   some_routine();
   failure_routine();
}

...then fail will never be set, since you exited some_routine 
before throwing any exceptions. Thus why you set "scope(failure)" 
on the main function, if you're concerned about globals being 
destructed due to program failure. You set "scope(failure)" on 
the local function when you're concerned about locals complaining 
as they destruct when the function returns from an exceptional 
situation. If you want the former and do the latter, then your 
globals will not see that any exception occurred. If you want the 
latter and do the former, then any local variables will destruct 
long before main reaches "scope(failure)"

YMMV. I haven't tested any of this, and I'm kind of shaky at D 
too.


More information about the Digitalmars-d-learn mailing list