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