DIP44: scope(class) and scope(struct)

Walter Bright newshound2 at digitalmars.com
Sat Aug 24 23:30:26 PDT 2013


On 8/24/2013 10:35 PM, H. S. Teoh wrote:
> On Sat, Aug 24, 2013 at 06:50:11PM -0700, Walter Bright wrote:
>> On 8/24/2013 1:09 PM, H. S. Teoh wrote:
>>> On Sat, Aug 24, 2013 at 12:27:37PM -0700, Walter Bright wrote:
>>> [...]
>>>> Not a bad idea, but it has some issues:
>>>>
>>>> 1. scope(failure) takes care of most of it already
>>>>
>>>> 2. I'm not sure this is a problem that needs solving, as the DIP
>>>> points out, these issues are already easily dealt with. We should be
>>>> conservative about adding more syntax.
>>>>
>>>> 3. What if the destructor needs to do more than just unwind the
>>>> transactions? Where does that code fit in?
>>>
>>> I think it's unhelpful to conflate scope(this) with dtors. They are
>>> related, but -- and I guess I was a bit too strong about saying dtors
>>> are redundant -- if we allow both, then scope(this) can be reserved
>>> for transactions, and you can still put code in ~this() to do
>>> non-trivial cleanups.
>>
>> If you take out automatic dtor generation, I see no difference
>> between scope(this) and scope(failure).
>>
>>
>>>> 4. The worst issue is the DIP assumes there is only one constructor,
>>> >from which the destructor is inferred. What if there is more than
>>>> one constructor?
>>>
>>> This is not a problem. If there is more than one constructor, then
>>> only those scope(this) statements in the ctor that were actually
>>> encountered will trigger when the object reaches the end of its
>>> lifetime.
>>
>> Do you mean multiple dtors will be generated, one for each
>> constructor?
>
> No. Given this code:
>
> 	class C {
> 		this(int) {
> 			scope(this) writeln("A1");
> 			scope(this) writeln("A2");
> 		}
>
> 		this(float) {
> 			scope(this) writeln("B1");
> 			scope(this) writeln("B2");
> 		}
> 	}
>
> The lowered code looks something like this:
>
> 	class C {
> 		this(int) {
> 			scope(failure) __cleanup();
>
> 			// scope(this) writeln("A1");
> 			// Translated into:
> 			__cleanups ~= { writeln("A1"); };
>
> 			// scope(this) writeln("A2");
> 			// Translated into:
> 			__cleanups ~= { writeln("A2"); };
> 		}
>
> 		this(float) {
> 			scope(failure) __cleanup();
>
> 			// scope(this) writeln("B1");
> 			// Translated into:
> 			__cleanups ~= { writeln("B1"); };
>
> 			// scope(this) writeln("B2");
> 			// Translated into:
> 			__cleanups ~= { writeln("B2"); };
> 		}
>
> 		void delegate()[] __cleanups;
>
> 		void __cleanup() {
> 			foreach_reverse (f; __cleanups)
> 				f();
> 		}
>
> 		~this() {
> 			__cleanup();
> 		}
> 	}
>
> So, there is only one dtor. But it automatically takes handles
> triggering the scope(this) statements of only the ctor that was actually
> used for creating the object. And if the ctor didn't successfully
> complete, it only triggers the scope(this) statements that have been
> encountered up to that point.
>
> Of course, the above lowered code is just to illustrate how it works.
> The actual implementation can be optimized by the compiler. E.g., if
> there is only one ctor and the sequence of scope(this) can be statically
> determined, then the cleanup statements can be put into the dtor
> directly without incurring the overhead of allocating the __cleanups
> array. If the cleanups don't need closure over ctor local variables,
> then they can just be function ptrs, not delegates. Only when you're
> doing something complicated do you actually need an array of delegates,
> which should be relatively rare.

Your example, again, is of an auto-generated dtor. But you said earlier that 
wasn't the point.

Without the auto-generated dtor, it is just scope(failure), which is already a D 
feature.

I don't get it.




More information about the Digitalmars-d mailing list