February 28, 2006
"Sean Kelly" <sean@f4.ca> wrote in message news:du225a$2549$1@digitaldaemon.com...
>>> But
>>> Transaction abc()
>>> {
>>>    try {
>>>      Foo f = dofoo();
>>>      Bar b = dobar();
>>>      Def d = dodef();
>>>      return Transaction(f, b, d);
>>>   } catch(object er) {
>>>        delete f; delete b; delete d;
>>>   }
>>> }
>>> is not worse to be honest.
>>
>> It is worse because it won't even compile. f, b, and d are not in scope in the catch statement. Even if they were, there's still a serious bug - if dofoo() throws an exception, then the catch statement will attempt to delete b and d, which are not even initialized yet.
>
> Is it really a bug to call delete on a null reference?  This is well-defined behavior in C++.

It's not even a null reference.


February 28, 2006
Walter Bright wrote:
> "Sean Kelly" <sean@f4.ca> wrote in message news:du225a$2549$1@digitaldaemon.com...
>>>> But
>>>> Transaction abc()
>>>> {
>>>>    try {
>>>>      Foo f = dofoo();
>>>>      Bar b = dobar();
>>>>      Def d = dodef();
>>>>      return Transaction(f, b, d);
>>>>   } catch(object er) {
>>>>        delete f; delete b; delete d;
>>>>   }
>>>> }
>>>> is not worse to be honest.
>>> It is worse because it won't even compile. f, b, and d are not in scope in the catch statement. Even if they were, there's still a serious bug - if dofoo() throws an exception, then the catch statement will attempt to delete b and d, which are not even initialized yet.
>> Is it really a bug to call delete on a null reference?  This is well-defined behavior in C++.
> 
> It's not even a null reference. 

Not in the above example, but then the above example doesn't even compile.  I assumed you were talking about something like this:

Foo f;
Bar b;
try {
    f = dofoo();
    b = dobar();
} catch( Object o ) {
    delete f; delete b;
}

It's quite possible for f and b to both be uninitialized, yet I would expect the delete calls to be well-defined anyway.

Sean
February 28, 2006
"Sean Kelly" <sean@f4.ca> wrote in message news:du25eb$288m$3@digitaldaemon.com...
> Not in the above example, but then the above example doesn't even compile. I assumed you were talking about something like this:
>
> Foo f;
> Bar b;
> try {
>     f = dofoo();
>     b = dobar();
> } catch( Object o ) {
>     delete f; delete b;
> }
>
> It's quite possible for f and b to both be uninitialized, yet I would expect the delete calls to be well-defined anyway.

delete is well defined, and works properly with null. For more complex unwinding, you'll need to add state variables to keep track of what has been set and what hasn't.

The new bug in the above code is that the catch block fails to rethrow o.

I hope that these buggy examples show just how hard it is to get try-catch-finally to be correct, and how easy it is to get the on_scope correct. This leads me to believe that try-catch-finally is just conceptually wrong, as it does not match up with how we think despite being in common use for over a decade.


February 28, 2006
Walter Bright wrote:
> "Sean Kelly" <sean@f4.ca> wrote in message news:du25eb$288m$3@digitaldaemon.com...
>> Not in the above example, but then the above example doesn't even compile. I assumed you were talking about something like this:
>>
>> Foo f;
>> Bar b;
>> try {
>>     f = dofoo();
>>     b = dobar();
>> } catch( Object o ) {
>>     delete f; delete b;
>> }
>>
>> It's quite possible for f and b to both be uninitialized, yet I would expect the delete calls to be well-defined anyway.
> 
> delete is well defined, and works properly with null. For more complex unwinding, you'll need to add state variables to keep track of what has been set and what hasn't.

That's all I was asking.  I wondered at the implications of:

"there's still a serious bug - if dofoo() throws an exception, then the catch statement will attempt to delete b and d, which are not even initialized yet"

> The new bug in the above code is that the catch block fails to rethrow o.

Arguably not a bug, as this could be intended behavior.  However, I would think it's clear that the above code was merely intended to clarify a previous statement.  I wouldn't suggest that it is correct or well-written :-)

> I hope that these buggy examples show just how hard it is to get try-catch-finally to be correct, and how easy it is to get the on_scope correct. This leads me to believe that try-catch-finally is just conceptually wrong, as it does not match up with how we think despite being in common use for over a decade.

I've been sold on Andrei's method since he first introduced it, so no issue there.  It's certainly far simpler and more meaningful than the backflips often required by RAII.  And while the proposed C++ shared_ptr syntax makes some progress for this purpose, it's still a far cry from on_scope_*, particularly when paired with inner functions.


Sean
February 28, 2006
On Wed, 01 Mar 2006 06:19:35 +1100, Walter Bright <newshound@digitalmars.com> wrote:


...

> I hope that these buggy examples show just how hard it is to get
> try-catch-finally to be correct, and how easy it is to get the on_scope
> correct. This leads me to believe that try-catch-finally is just
> conceptually wrong, as it does not match up with how we think despite being in common use for over a decade.

Execellent point, Walter. The concept of scope guards in D is very exciting and a real winner in my opinion. I'm looking forward to the syntax improving and stabilizing so I can use it in real applications.

And may I be so bold as to paraphase you ...

  : I hope that these buggy examples show just how hard it is to get
  : C-style booleans to be correct, and how easy it is to get the semantically
  : boolean correct. This leads me to believe that the C-style boolean is just
  : conceptually wrong, as it does not match up with how we think despite
  : being in common use for many decades.

<G><G><G>

-- 
Derek Parnell
Melbourne, Australia
March 01, 2006
"Walter Bright" <newshound@digitalmars.com> wrote in message news:du2925$2cvj$1@digitaldaemon.com...
>
> "Sean Kelly" <sean@f4.ca> wrote in message news:du25eb$288m$3@digitaldaemon.com...
>> Not in the above example, but then the above example doesn't even compile. I assumed you were talking about something like this:
>>
>> Foo f;
>> Bar b;
>> try {
>>     f = dofoo();
>>     b = dobar();
>> } catch( Object o ) {
>>     delete f; delete b;
>> }
>>
>> It's quite possible for f and b to both be uninitialized, yet I would expect the delete calls to be well-defined anyway.
>
> delete is well defined, and works properly with null. For more complex unwinding, you'll need to add state variables to keep track of what has been set and what hasn't.
>
> The new bug in the above code is that the catch block fails to rethrow o.

Let's say it was an intention. Pretty common by the way.

>
> I hope that these buggy examples show just how hard it is to get try-catch-finally to be correct, and how easy it is to get the on_scope correct. This leads me to believe that try-catch-finally is just conceptually wrong, as it does not match up with how we think despite being in common use for over a decade.

Well try first to explain what will happen on
on_scope_success { delete baz; throw foo; }
on_scope_exit { delete baz; throw foo; }

What "lexical order" will be used here? What scope guards will be invoked
and so on.
Having on_scope_*** spread all other the "... lots of code ..."  will create
code maintainance nightmare as to trace visually what will happen and when
will not
be a task for human anymore.

And as far as I understand main idea of on_scope_exit is a sort of poor man
struct destructor...
I suspect that you are thinking about how to remove 'auto'/RAII, right?

Andrew.
http://terrainformatica.com








March 01, 2006
Chris Miller wrote:
> On Mon, 27 Feb 2006 17:16:38 -0500, Dawid Ciężarkiewicz  <dawid.ciezarkiewicz@gmail.com> wrote:
> 
>> Chris Miller wrote:
>>
>>> This format looks good to me:
>>>
>>> scope(exit)  foo();
>>> scope(success)  bar();
>>> scope(failure)  baz();

This gets my vote too.

And then it would look natural, if you ever need more than one function run:

scope(failure) foo();
scope(success) { bar(); haveAParty(); writeHome(); }
scope(exit) yawn();

>> Yeap. It's nice. Maybe s/success/pass/ s/failure/fail/ would even  improve it
>> a little.
> 
> Good one.

That's kind of neat, too. Having all three alternatives with same number of characters, makes it tidy:

scope(exit)  foo();
scope(pass)  bar();
scope(fail)  baz();

Except of course, that "pass" might look like "skip" to somebody new to D.
March 01, 2006
"Derek Parnell" <derek@psych.ward> wrote in message news:op.s5pd12y86b8z09@ginger.vic.bigpond.net.au...
> <G><G><G>

Nice try :-)


March 01, 2006
On 2006-02-28 00:08:01 -0800, "Andrew Fedoniouk" <news@terrainformatica.com> said:

> 
> "S. Chancellor" <dnewsgr@mephit.kicks-ass.org> wrote in message news:du0lmk$dbg$1@digitaldaemon.com...
>> On 2006-02-25 18:06:36 -0800, "Walter Bright" <newshound@digitalmars.com> said:
>> 
>>> Scope guards are a novel feature no other language has. They're based on Andrei Alexandrescu's scope guard macros, which have led to considerable interest in the idea. Check out the article www.digitalmars.com/d/exception-safe.html
>> 
>> The only thing I see that's amazingly useful about this is the on_scope_success.  Having a block of code that is only executed when an exception is NOT thrown would be nice.  However, the rest of this stuff seems like rocks under the water.  Your example of the new programmer coming in reads like this to me:  "The new programmer may not take the time to actually read the code he's modifying, so lets stick hidden stuff in there to take care of things he might have missed."  Which doesn't seem very logical to me, as it may be just as important to modify those on success/on failure blocks and miss them.
>> 
>> I'd say add another option to try..catch..finally paradigm.
>> 
>> -S.
>> 
> 
> try {
> 
>    something horrible here....
> 
>    //on_scope_success:
> 
>    ... and here is on success part
> 
> }
> 
> Why do you need separate 'passed' part?
> 
> Andrew.

You don't.  I wasn't thinking clearly.  I guess I was confused by the fact that there was an on_scope_success in this new addition.

-S.

March 01, 2006
On 2006-02-27 23:17:06 -0800, "Regan Heath" <regan@netwin.co.nz> said:

> On Tue, 28 Feb 2006 06:34:00 +0000 (UTC), Tom Johnson  <Tom_member@pathlink.com> wrote:
>>> I'd say add another option to try..catch..finally paradigm.
>>> 
>>> -S.
>> 
>> Seconded.  For more fun, next we can debate whether the syntax should be:
>> 
>> 1.  try..pass..catch..finally
>> 2.  try..catch..pass...finally
>> 3.  try..catch..finally..pass
> 
> No, on_scope gives us more than try/catch/finally. Let me try this another  way.
> 
> "catch" from try/catch/finally allows:
>   - you to execute a static/pre-defined set of code in the event that there  is a failure in the current scope.
> 
> "finally" from try/catch/finally allows:
>   - you to execute a static/pre-defined set of code at the exit of the  current scope in all cases.
> 
> Compare that to:
> 
> on_scope_failure allows:
>   - you to add one or more sets of code, at the points at which they become  required, to the list of things to execute in the event of a failure.
> 
> on_scope_exit allows:
>   - you to add one or more sets of code, at the points at which they become  required, to the list of things to execute at the exit of the scope in all  cases.
> 
> To achieve the same thing that on_scope gives with try/finally requires  you to store state somewhere to indicate which parts of the finally block  to execute, or, it requires that you define several finally blocks and  nest them. Both of those options are no where near as neat as on_scope.
> 
> I'm honestly baffled that people can't see the difference.
> 
> Regan

Ah.  I see what you want.  I was a bit confused about this before.  This is just syntatic sugar to a relatively easy to write class though. Maybe it should be part of the standard library?

It essentially acts like a multi-homed delegate which is implicitly called at the end of the scope.
Something like this would be equivalent?

Transaction abc()
{
	Foo f;
    	Bar b;
    	Def d;
	Auto scoped = new Scoper();  //scoped.exit() can be called by the destructor.  We won't add a finally block.

	try {
		scoped.failures ~= void delegate () { dofoo_unwind(f); }
    		f = dofoo();
		
		scoped.failures ~= void delegate () { dobar_unwind(b); }
		b = dobar();
		
		scoped.success()
	} catch (Exception o) {
		scoped.failed()
		throw o;
	}

	return Transaction(f, b, d);
}

It seems to me Walter's class example doesn't exactly do this justice.  I find this fully acceptable for the few places I would use this.  The implicit instantiation of an object like this might be nice though, if it only added the extra code when it was referenced.  Which should be trivial to do.  I'd much rather have an "implicit" scope object than this, or the current syntax.

-S.