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.

Reply via email to