scope(exit) considered harmful

Justin Johansson no at spam.com
Sun Nov 8 17:04:43 PST 2009


Andrei Alexandrescu Wrote:

> Justin Johansson wrote:
> > dsimcha Wrote:
> > 
> >> Hey, I never programmed at all seriously in C++ before coming to D and I somehow
> >> figured out scope(exit).  I'm not even sure you'd be able to pry scope(exit) out
> >> of my cold, dead hands.  I might super-glue it to my hands on my death bed.
> >>
> >> Example of where you could do stuff with scope statements that would be **almost
> >> impossible** with try/catch/finally:
> >>
> >> enum myConvenientMixin =
> >> q{
> >>     myRegionAllocator.init();
> >>     scope(exit) myRegionAllocator.destroy();
> >> };
> >>
> >> void someFunction() {
> >>     mixin(myConvenientMixin);
> >>
> >>     // Build some really complicated data structure using
> >>     // myRegionAllocator.
> >> }
> >>
> >> With finally only, myRegionAllocator would be so horribly ugly to use in the
> >> presence of throwing code that noone would ever even consider doing such a thing.
> >>  With scope(exit) and mixins, you've solved the problem of the memory getting
> >> freed in one line of code and don't even need to think about it when writing the
> >> rest of your function.
> > 
> > I know what you mean, but it's not like "D" stands for "Discovered America".
> > scope(exit) is just some syntactic sugar which stack allocated objects in C++
> > have been able to do since Bjarne Stroustrup (C++ inventor) was in knee-high
> > locks, umm, socks :-)
> > 
> > class Lock
> > {
> >   Foo foo;
> > 
> >   Lock( Foo aFoo): foo( aFoo) {
> >    // lock Foo resource
> >    lock( foo);
> >  }
> > 
> >   ~Lock() {
> >   // unlock Foo resource
> >    unlock( foo);
> >   }
> > }
> > 
> > 
> > void myFunc( Foo foo)
> > {
> >   Lock( foo);
> > 
> >   // do something with the locked resource
> >   // that might throw an exception
> >   // ... processing()
> > 
> >   // Note that the Foo resource is acquired and
> >   // upon entry to this function and released
> >   // when the function scope exits irrespective
> >   // of whether an exception is thown by
> >   // processing() or not.
> > }
> > 
> > No sign of any ugly try/finally here; yet achieves safe outcome.
> > 
> > C++ constructors/destructors and stack-unwinding mechanism
> > are considered FREAKING AWESOME.
> > 
> > -- Justin :-)
> > 


> I can't process how this is great and scope(exit) isn't. For sure, 
> putting a Lock inside an if (true) statement will make the destructor 
> get called at the scope's exit (let me emphasize: *scope's exit*), not 
> function's exit. That is, in your rant you could easily replace 
> scope(exit) with scoped objects and get the same frustration.
> 
> Andrei

"I can't process how this is great and scope(exit) isn't"

Be reassured that I am asking myself the same question as to
why I feel/felt this way given that scoped objects and scope(exit)
can be used for similar purposes.

I'm not trying to "get out of this one" but believe me that
my honest intuition* is counter to my knowledge of how
scope(exit) works so I will try to tease this out.

*That intuition is that scope(exit) works at function scope
as opposed to block scope (and of course intuition is allowed
to be wrong).

About 1 week after getting into D (some 2 months ago), it
became second nature to me to set up a scope(exit) upon
entry into a function in cases which would otherwise requiring
try/finally.

After a while the pattern set in and I think that perhaps
I stopped thinking about it meaning curly-brace block scope
and function scope instead.  This is reinforced by the fact
that I had never used scope(exit) in a nested block (such
as an if statement) before this incident .. which obviously
turned out to be my mistake.

It is possible that I formed a mental image of scope(exit)
as being analogous to C's atexit()** function which operates
at "process-scope".

(**For those that don't know C, atexit() takes a function
pointer parameter and effectively schedules a call to this
function when the application/process ends.)

The other reason I think that I think that scoped objects and
scope(exit) "feel" different is that scoped objects (in D)
or stack-allocated objects in C++ (RAII) is that the former
are "declarative" in nature whereas scope(exit) feels more
"instructive" .. i.e. instruct the program to take this
course of action when something finishes (block scope exit in
this case).  In the former case, the (constructor/destructor)
behaviour is virtually a side-effect of, or implicit in,
the declaration.  There is nothing implicit about scope(exit).
It usage is making an explicit statement.  Therefore I
think that it is a reasonable hypothesis that models may
well arise in the minds of users that view the two constructs
differently.

Lastly consider this situation for which the D solution is,
in my opinion, counter-intuitive.

You have a function that takes two nullable "lockable-resource"
parameters.  The requirement is to lock/unlock the
respective resources if they are non-null before doing
some processing.


My intuitive solution as follows is incorrect as it
assumes function scope for the envoking the exit
action:

void foo( Resource res1, Resource res2)
{
   if (res1 !is null) {
     lock( res1);
     scope(exit) unlock( res1);
   }

   if (res2 !is null) {
     lock( res2);
     scope(exit) unlock( res2);
   }

   // do some processing given the resources
   // are now appropriately locked and will
   // be released when done with

   // ...
}


I think the correct solution using D's scope(exit)
should be more like:

void foo( Resource res1, Resource res2)
{
   if (res1 !is null)
     lock( res1);

   scope(exit) if (res1 !is null) unlock( res1);

   if (res2 !is null)
     lock( res2);

   scope(exit) if (res2 !is null) unlock( res2);

   // do some processing given the resources
   // are now appropriately locked and will
   // be released when done with

   // ...
}


Assuming that the latter solution is indeed correct for
the given problem scenario, I maintain that it doesn't
seem intuitive and that there is a valid use case for
some hypothetical construct like

function-scope(exit) or scope(function-exit)


Maybe I'm on a different planet but nevertheless
look forward to yr comments.

-- Justin Johansson







More information about the Digitalmars-d mailing list