February 04, 2016
On Thursday, 4 February 2016 at 10:03:13 UTC, Jonathan M Davis wrote:
> On Wednesday, February 03, 2016 23:55:42 Ali Çehreli via Digitalmars-d-learn wrote:
>> std::uncaught_exception used to be considered useless:
>
> I think that the only case I've ever had for it was for a unit testing framework where I wanted to use RAII to print something when the tests failed (and thus an exception was thrown) but not print if they succeeded, and if I were to do that in D, I'd just use scope(failure).

Well, yes, it is useful for logging. It is useful to know if an invariant is broken during regular unwinding (serious error) or exceptional situations (might be unavoidable).

But I think Herb Sutters major point was that if you had multiple destructors it could not detect where the exceptions originate from. So C++17 has a new function that returns the number of uncaught expressions:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf

February 05, 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.
February 05, 2016
On Friday, 5 February 2016 at 07:31:24 UTC, cy wrote:
> I think you might be talking about two very different concepts here. Unwinding only happens within the context of a certain scope.

The object itself is the scope (RAII). If you can test for "uncaught_exceptions" you can implement the equivalent of scope(failure/success) etc within destructors.

So you basically should be able to do:

{
   Database db ...;

   for(...){
     db.Transaction t = db.new_transaction(....)


     // transaction t is closed here by destructor
     // but should not commit if exception is unwinding
   }

  // db is closed here but should log different information for exceptions
}


February 06, 2016
On Friday, 5 February 2016 at 08:16:05 UTC, Ola Fosheim Grøstad wrote:
> If you can test for "uncaught_exceptions" you can implement the equivalent of scope(failure/success) etc within destructors.

Sorry, years of python programming have made me shy of destructors. It just looks a little less "magic" to me if I specify the destruction explicitly after creating the object, using the "scope(exit)" syntax. "scope(success)" and "scope(failure)" have no analogy in destructors as far as I know. Destructors just destroy the object, and doing more than that is risking weird edge conditions.

But again, if you already have your elaborate destructor, you can set "o.fail = true" in a "scope(failure)" statement, and the destructor just has to check this.fail for how it's being destroyed..
February 06, 2016
On Saturday, 6 February 2016 at 06:08:41 UTC, cy wrote:
> Sorry, years of python programming have made me shy of destructors. It just looks a little less "magic" to me if I specify the destruction explicitly after creating the object, using the "scope(exit)" syntax.

That is error-prone!

In Python you are supposed to use the with-statement to get RAII like behaviour. So you use __enter__ and __exit__ methods on the context-manager. From the Python docs:

«The context manager’s __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.»

See, even Python supports this. :-)

February 06, 2016
On Saturday, 6 February 2016 at 14:25:21 UTC, Ola Fosheim Grøstad wrote:
> See, even Python supports this. :-)

And D supports the "with" statement in python, in the form of "scope()" statements. The D way is slightly less misleading too, as

with somethingThatFails() as e:
  print(e)

doesn't make it obvious that neither __enter__ nor __exit__ will be called if you raise an exception in the creation of the object, or that if you raise an exception in __enter__, then __exit__ won't be called.

auto e = somethingThatFails()
scope(failure) cleanup(e);

makes more sense to me, since it's blatantly obvious that the construction (and entering) process isn't covered by the cleanup routine.
February 07, 2016
On Saturday, 6 February 2016 at 22:51:45 UTC, cy wrote:
> auto e = somethingThatFails()
> scope(failure) cleanup(e);
>
> makes more sense to me, since it's blatantly obvious that the construction (and entering) process isn't covered by the cleanup routine.

Not sure what you mean by that. Destructors shall not be called if constructors fail, constructors should clean up themselves.

"scope(failure)" is useful for C-like APIs, but not a good replacement for RAII.

1 2
Next ›   Last »