www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - extension to scope

reply Alex Burton <alexibu.nospamplease mac.com> writes:
I would like to say that the ability to declare a class reference as scope,
which guarantees that the destructor is called when exceptions occur, is a
great feature that allows much more elegant code than putting try catches every
where.

However I think that it is somewhat limited in that references to other objects
held by the class are not guaranteed to be freed.

I would like to see the scope keyword's meaning extended so that any objects
referenced by the scoped class are freed if no other references to them exist,
and that this is done recursively.

This would allow the logical use of scope beyond simple little convienience
classes to large transaction type objects which need to be rolled back when an
exception occurs.

This is of great importance to me when I have classes representing a
transaction that may use some real resources such as files, ports etc.

Using an example I would like the code below to output 
~C
~B
~A
finished

import std.stdio;

class A
{
	~this() { writefln("~A"); }		
};

class B
{
	A a;
	this() { a = new A(); }	
	~this() { writefln("~B"); }		
};

class C
{
	B b;
	this() { b = new B(); }
	~this() { writefln("~C"); }		
};

int main()
{
	{
		scope auto x = new C;
	}
	writefln("finished");
	return 0;
}
Jan 28 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Alex Burton wrote:
 I would like to say that the ability to declare a class reference as scope,
which guarantees that the destructor is called when exceptions occur, is a
great feature that allows much more elegant code than putting try catches every
where.
 
 However I think that it is somewhat limited in that references to other
objects held by the class are not guaranteed to be freed.
 
 I would like to see the scope keyword's meaning extended so that any objects
referenced by the scoped class are freed if no other references to them exist,
and that this is done recursively.

The "if no other references exist" part can be a bit hard to determine without running a full GC cycle or some tricky analysis by the compiler.
 This would allow the logical use of scope beyond simple little convienience
classes to large transaction type objects which need to be rolled back when an
exception occurs.
 
 This is of great importance to me when I have classes representing a
transaction that may use some real resources such as files, ports etc.

If you ensure that no other references exist, you can just tell the members to clean up after themselves and then delete them. See the modifications I made to your code:
 Using an example I would like the code below to output 
 ~C
 ~B
 ~A
 finished
 
 import std.stdio;
 
 class A
 {
 	~this() { writefln("~A"); }		
 };
 
 class B
 {
 	A a;

private A a; // to make sure no other references exist /// Call this to clean up before deleting instances of /// this class, to clean up resources. void close() { delete a; }
 	this() { a = new A(); }	
 	~this() { writefln("~B"); }		
 };
 
 class C

scope class C // to make sure it never gets deleted by the GC
 {
 	B b;

 	this() { b = new B(); }
 	~this() { writefln("~C"); }		

Because of the 'scope' at the start of the class declaration, it is now safe to reference member objects in the destructor. (That's undefined behavior when it's called by the GC, but scoped classes don't get deleted by the GC) So replace destructor with: ~this() { b.close; delete b; writefln("~C"); }
 };
 
 int main()
 {
 	{
 		scope auto x = new C;
 	}
 	writefln("finished");
 	return 0;
 }

You *could* do all the deletions in the destructors, but then you need to make damn sure your objects won't _ever_ be deleted by the GC. P.S. Ideally, of course, you'd be allowed to declare members as 'scope', so that they always get deleted when the referencing object is. Unfortunately, this is not currently allowed. (A way to implement this would be to 'inline' scoped members)
Jan 28 2007
parent Max Samukha <samukha voliacable.com> writes:
P.S.
Ideally, of course, you'd be allowed to declare members as 'scope', so 
that they always get deleted when the referencing object is. 
Unfortunately, this is not currently allowed.
(A way to implement this would be to 'inline' scoped members)

It seems like this feature would beat the .NET Framework's disposal/finalization approach. Would it be hard to implement in the compiler?
Jan 29 2007