Jump to page: 1 2 3
Thread overview
Re: DIP44: scope(class) and scope(struct)
Aug 24, 2013
H. S. Teoh
Aug 24, 2013
H. S. Teoh
Aug 24, 2013
Tobias Pankrath
Aug 24, 2013
Tobias Pankrath
Aug 24, 2013
Ramon
Aug 24, 2013
Simen Kjaeraas
Aug 26, 2013
H. S. Teoh
Aug 27, 2013
Tobias Pankrath
Aug 27, 2013
Dmitry Olshansky
Aug 27, 2013
H. S. Teoh
Aug 27, 2013
deadalnix
Aug 27, 2013
H. S. Teoh
Aug 28, 2013
deadalnix
Aug 28, 2013
H. S. Teoh
Aug 27, 2013
Dmitry Olshansky
Aug 27, 2013
H. S. Teoh
Aug 27, 2013
Dmitry Olshansky
Aug 27, 2013
H. S. Teoh
Aug 26, 2013
H. S. Teoh
August 24, 2013
On Sat, Aug 24, 2013 at 02:48:45AM +0200, Andrej Mitrovic wrote:
> On Saturday, 24 August 2013 at 00:45:46 UTC, H. S. Teoh wrote:
> >Destroy! ;-)
> 
> I won't destroy but I'll say avoid the struct/class keywords and use `scope(this)` instead, it looks nicer and will work easier in generic code.

Good point! I'll update the DIP to use scope(this) instead. It does look
nicer. And yes, it's important for generic code. :)


T

-- 
Nothing in the world is more distasteful to a man than to take the path that leads to himself. -- Herman Hesse
August 24, 2013
On Sat, Aug 24, 2013 at 06:19:25AM +0200, Meta wrote:
> "With this extension to scope guards, class and struct destructors will practically not be needed anymore, since scope(this) will take care of cleaning up everything."
> 
> I can't think of an example off the top of my head, but is it really okay to conflate destruction due to an error during construction, and destruction over the regular course of a struct's usage? What if one instance requires different code from the other? Maybe require that scope(this) statements *only* be run if there is an error during construction, while just the destructor will be run normally?

If a particular cleanup operation only applies to a ctor failure, you could just use scope(failure) in the ctor, and put the different cleanup code in the dtor.

scope(this) is really meant to encapsulate the usual (I think!) case
where the same cleanup code applies in both cases.

Maybe I was a bit too strong to say dtors won't be needed anymore, but if you leave dtors in, then they can still handle this case. :)


T

-- 
May you live all the days of your life. -- Jonathan Swift
August 24, 2013
On 24/08/13 02:44, H. S. Teoh wrote:
> I've written up a proposal to solve the partially-constructed object
> problem[*] in D in a very nice way by extending scope guards

Converse problem: suppose that you need certain resources to be _created_ in order for the struct to work correctly.  Consider:

    struct Foo
    {
        Resource res;

        this(int n)
        {
            res = getResource(n);
        }
    }

    void main()
    {
        auto foo1 = Foo(10);  // Good! Resource is initialized
        Foo foo2;   // Bad! Resource is uninitialized
    }

... where we can probably reasonably assume that getResource is doing some kind of allocation.

Is there any way to handle this needed-at-entry-to-scope requirement, in a similar way to how your DIP handles exit of scope?
August 24, 2013
On Saturday, 24 August 2013 at 13:34:06 UTC, Joseph Rushton Wakeling wrote:
> On 24/08/13 02:44, H. S. Teoh wrote:
>> I've written up a proposal to solve the partially-constructed object
>> problem[*] in D in a very nice way by extending scope guards

> Is there any way to handle this needed-at-entry-to-scope requirement, in a similar way to how your DIP handles exit of scope?

Invariants?
August 24, 2013
On 24/08/13 15:55, Tobias Pankrath wrote:
> Invariants?

Don't they get stripped out in -release code?

August 24, 2013
On Saturday, 24 August 2013 at 14:40:36 UTC, Joseph Rushton Wakeling wrote:
> On 24/08/13 15:55, Tobias Pankrath wrote:
>> Invariants?
>
> Don't they get stripped out in -release code?

Yes. But the example code that you presented looked to me like logic/coding errors. Invariants should be good enough here. If you want to pay for the added safety, because you think your code isn't tested well enough at all, you can always compile without debug.
August 24, 2013
On Sat, 24 Aug 2013 15:55:52 +0200, Tobias Pankrath <tobias@pankrath.net> wrote:

> On Saturday, 24 August 2013 at 13:34:06 UTC, Joseph Rushton Wakeling wrote:
>> On 24/08/13 02:44, H. S. Teoh wrote:
>>> I've written up a proposal to solve the partially-constructed object
>>> problem[*] in D in a very nice way by extending scope guards
>
>> Is there any way to handle this needed-at-entry-to-scope requirement, in a similar way to how your DIP handles exit of scope?
>
> Invariants?

I would also like to mention @disable this();

-- 
Simen
August 24, 2013
On 24/08/13 18:21, Simen Kjaeraas wrote:
> I would also like to mention @disable this();

Yes, I know, but suppose that I don't _want_ to disable this() ... ?

Really this is one of those cases where a parameter-free constructor would be useful.

August 24, 2013
I'd seriously *hate* to abuse invariants for that. The job of invariants is to give guarantees about state - not to manage e.g. allocations, creations, cleanups, etc.

"Has something properly succeeded and if not is there cleanup needed?" is the job of scope and possibly (but ugly and non-elegantly) exceptions.
August 26, 2013
On Sun, Aug 25, 2013 at 12:18:27AM +0200, Tobias Pankrath wrote:
> On Saturday, 24 August 2013 at 20:11:14 UTC, H. S. Teoh wrote:
> >How would you use RAII to solve this problem? If I have a class:
> >
> >	class C {
> >		Resource1 res1;
> >		Resource2 res2;
> >		Resource3 res3;
> >		this() {
> >			...
> >		}
> >	}
> >
> >How would you write the ctor with RAII such that if it successfully inits res1 and res2, but throws before it inits res3, then only res1 and res2 will be cleaned up?
> 
> Like someone else already proposed: Using a wrapper type W that releases the resources in it's destructor. W.init wouldn't release anything. So by definition (if I recall the rules correctly) every new instance of C would have res3 = Resource3.init prior to it's constructor called.
> 
> Now make sure that a) res3's destructor gets called (check) b)
> res3's destructor may be called on Resource3.init. That would be a
> new rule similar to 'no internal aliasing' and c) that every
> constructor of C either sets res3 to a destructable value or does
> not touch it at all ('transactional programming').
[...]

But don't you still need to manually cleanup in the case of ctor failure? AFAIK, the dtor is not invoked on the partially-constructed object if the ctor throws. So you'd still have to use scope(failure) to manually release the resource.

To prove my point, here is some sample code that (tries to) use RAII to
cleanup:

	import std.stdio;

	struct Resource {
		int x = 0;
		this(int id) {
			x = id;
			writefln("Acquired resource %d", x);
		}
		~this() {
			if (x == 0)
				writefln("Empty resource, no cleanup");
			else
				writefln("Cleanup resource %d", x);
		}
	}

	struct S {
		Resource res1;
		Resource res2;
		Resource res3;

		this(int) {
			res1 = Resource(1);
			res2 = Resource(2);
			assert(res1.x == 1);
			assert(res2.x == 2);

			throw new Exception("ctor failed!");
			res3 = Resource(3);	// not reached
			assert(res3.x == 3);	// not reached
		}
	}

	void main() {
		try {
			auto s = S(123);
		} catch(Exception e) {
			writeln(e.msg);
		}
	}

Here is the program output:

	Acquired resource 1
	Empty resource, no cleanup
	Acquired resource 2
	Empty resource, no cleanup
	ctor failed!

As you can see, the two resources acquired in the partially-constructed object did NOT get cleaned up. So, RAII doesn't work in this case.

The two interspersed cleanups were presumably cause by Resource.init being destructed when res1 and res2 were assigned to. But after being assigned to, res1 and res2's dtors were NOT invoked.

So I think my scope(this) idea has some merit, since it will correctly
handle this case. :)


T

-- 
Many open minds should be closed for repairs. -- K5 user
« First   ‹ Prev
1 2 3