Jump to page: 1 2 3
Thread overview
important proposal: scope keyword for class members
Mar 07, 2009
John Simon
Mar 07, 2009
dsimcha
Mar 07, 2009
Christopher Wright
Mar 07, 2009
John Simon
Mar 07, 2009
Christopher Wright
Mar 07, 2009
John Simon
Mar 07, 2009
Sean Kelly
Mar 07, 2009
Sean Kelly
Mar 07, 2009
John Simon
Mar 08, 2009
Sean Kelly
Mar 08, 2009
John Simon
Mar 08, 2009
Sean Kelly
Mar 08, 2009
Bill Baxter
Mar 09, 2009
Sergey Gromov
Mar 09, 2009
Yigal Chripun
Mar 09, 2009
John Simon
Mar 07, 2009
Sean Kelly
Mar 07, 2009
Christopher Wright
Mar 08, 2009
Sean Kelly
Mar 09, 2009
Denis Koroskin
Mar 09, 2009
John Simon
Mar 10, 2009
Sean Kelly
Mar 12, 2009
John Simon
March 07, 2009
I'd like to propose a new use for the 'scope' keyword within an aggregate body.

Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.

Example:

class Inner {
    this() {}
    this(int x) {}
}

class Container {
    Inner i1; // initialized to null
    scope Inner i2; // allocated within class
    scope i3 = new Inner(42); // allocated within class

    this() {
        // implicit i2.__ctor();
        // i3.__ctor(42) written above, executed here
        assert(i1 is null);
        assert(i2 !is null);
        i1 = new Inner;
    }

    this(int overloaded) {
        // i2 and i3 are constructed, same as above
    }

    ~this() {
        // implicit i2.__dtor();
        // implicit i3.__dtor();
        // i1 is still somewhere in the heap
    }
}

IN ENGLISH:

If it's likely that class members will be constructed with and die with the object, why not just allocate them right in the class? Save on heap fragmentation and cache misses.  I was honesetly flabberghasted when I realized there was no way to do this in D, it seems like it should be one of the most basic constructs of a C-derived (or any low-ish level) language.

The programmer knows best where he wants his objects stored. Also, the 'scope' makes sense, and we should mirror the current behavior of the keyword in function bodies.
March 07, 2009
== Quote from John Simon (zildjohn01@gmail.com)'s article
> I'd like to propose a new use for the 'scope' keyword within an aggregate body. Members of class type declared with the scope keyword are allocated not as
references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.
> Example:
> class Inner {
>     this() {}
>     this(int x) {}
> }
> class Container {
>     Inner i1; // initialized to null
>     scope Inner i2; // allocated within class
>     scope i3 = new Inner(42); // allocated within class
>     this() {
>         // implicit i2.__ctor();
>         // i3.__ctor(42) written above, executed here
>         assert(i1 is null);
>         assert(i2 !is null);
>         i1 = new Inner;
>     }
>     this(int overloaded) {
>         // i2 and i3 are constructed, same as above
>     }
>     ~this() {
>         // implicit i2.__dtor();
>         // implicit i3.__dtor();
>         // i1 is still somewhere in the heap
>     }
> }
> IN ENGLISH:
> If it's likely that class members will be constructed with and die with the
object, why not just allocate them right in the class? Save on heap fragmentation and cache misses.  I was honesetly flabberghasted when I realized there was no way to do this in D, it seems like it should be one of the most basic constructs of a C-derived (or any low-ish level) language.
> The programmer knows best where he wants his objects stored. Also, the 'scope'
makes sense, and we should mirror the current behavior of the keyword in function bodies.

But the whole point of classes is that they're supposed to be polymorphic.  If you
don't need polymorphism, that's what structs are for.  You can store them either
inline (default) or in separate heap space (using pointers).  If you do need
polymorphism, you don't know at compile time what the size of the object is
supposed to be, and initializing the object the way you suggest defeats polymorphism.
March 07, 2009
dsimcha wrote:
> But the whole point of classes is that they're supposed to be polymorphic.  If you
> don't need polymorphism, that's what structs are for.  You can store them either
> inline (default) or in separate heap space (using pointers).  If you do need
> polymorphism, you don't know at compile time what the size of the object is
> supposed to be, and initializing the object the way you suggest defeats polymorphism.

scope can't use polymorphism. It's for situations in which you need your polymorphic type to be on the stack for whatever reason, but you don't need polymorphism at the moment.

For example, you want to open a file. File inherits from some sort of Stream class; it's not a struct. So you can write:

auto file = new File(path);
scope(exit) file.close;

Or:
scope file = new File(path);
March 07, 2009
Christopher Wright Wrote:

> dsimcha wrote:
> > But the whole point of classes is that they're supposed to be polymorphic.  If you
> > don't need polymorphism, that's what structs are for.  You can store them either
> > inline (default) or in separate heap space (using pointers).  If you do need
> > polymorphism, you don't know at compile time what the size of the object is
> > supposed to be, and initializing the object the way you suggest defeats polymorphism.
> 
> scope can't use polymorphism. It's for situations in which you need your polymorphic type to be on the stack for whatever reason, but you don't need polymorphism at the moment.
> 
> For example, you want to open a file. File inherits from some sort of Stream class; it's not a struct. So you can write:
> 
> auto file = new File(path);
> scope(exit) file.close;
> 
> Or:
> scope file = new File(path);

Exactly. And on top of that, how does being 'scope' limit its polymorphism? Code time:

int main(char[][] args) {
    class A {}
    class B : A {}
    class C : B {}

    void tryB(A a) {writefln(cast(B)a ? "casted" : "null");}

    scope a = new A;
    scope b = new B;
    assert(cast(C)b is null); // OK
    assert(cast(A)b == b); // OK

    tryB(a); // "null"
    tryB(b); // "casted"

    return 42;
}

You can always upcast and downcast safely, since it isn't actually a 'value' type, only the address is passed around. The type of the class remains intact.

So I don't see why this shouldn't be extended to classes as aggregate members.
March 07, 2009
John Simon wrote:
> I'd like to propose a new use for the 'scope' keyword within an aggregate body.
> 
> Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.

A while back, Walter said that he planned to do exactly this.  I'm not sure what the timetable is though, or if plans have changed.
March 07, 2009
Sean Kelly wrote:
> John Simon wrote:
>> I'd like to propose a new use for the 'scope' keyword within an aggregate body.
>>
>> Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.
> 
> A while back, Walter said that he planned to do exactly this.  I'm not sure what the timetable is though, or if plans have changed.

Oh, I should mention that I'm not sure how the compiler would handle this scenario:

class A { byte[16]; }
class B { byte[32]; }
class C {
    this( bool b ) {
        if( b ) o = new A;
        else    o = new B;
    }
    scope Object o;
}

If I had to guess I'd say that the compiler would either reserve the max size necessary to store both A or B, or that in non-trivial cases it just wouldn't bother with reserving space for o at all.
March 07, 2009
Sean Kelly wrote:
> John Simon wrote:
>> I'd like to propose a new use for the 'scope' keyword within an aggregate body.
>>
>> Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.
> 
> A while back, Walter said that he planned to do exactly this.  I'm not sure what the timetable is though, or if plans have changed.

I'd be happier if we investigated scope in classes as an ownership mechanism. In-situ storage is nice, but ownership management is more important.

Andrei
March 07, 2009
Andrei Alexandrescu wrote:
> Sean Kelly wrote:
>> John Simon wrote:
>>> I'd like to propose a new use for the 'scope' keyword within an aggregate body.
>>>
>>> Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.
>>
>> A while back, Walter said that he planned to do exactly this.  I'm not sure what the timetable is though, or if plans have changed.
> 
> I'd be happier if we investigated scope in classes as an ownership mechanism. In-situ storage is nice, but ownership management is more important.

Yeah, in-situ storage would just be a QOI feature like it is for scope variables at function level.  I agree that the logical effect of scope at class level is more important.
March 07, 2009
John Simon wrote:
> You can always upcast and downcast safely, since it isn't actually a 'value' type, only the address is passed around. The type of the class remains intact.

Right, but a scope variable has its type decided at compile time, always. In that way, it's not polymorphic.
March 07, 2009
Sean Kelly wrote:
> Andrei Alexandrescu wrote:
>> Sean Kelly wrote:
>>> John Simon wrote:
>>>> I'd like to propose a new use for the 'scope' keyword within an aggregate body.
>>>>
>>>> Members of class type declared with the scope keyword are allocated not as references or pointers, but initialized directly inside the container.  Instead of a default initializer of 'null', it will initialize with the default constructor of the class, or an optional assignment after the declaration. Any 'new [type]' within the assignment will resolve to a simple call to the type's __ctor, instead of a memory allocation.
>>>
>>> A while back, Walter said that he planned to do exactly this.  I'm not sure what the timetable is though, or if plans have changed.
>>
>> I'd be happier if we investigated scope in classes as an ownership mechanism. In-situ storage is nice, but ownership management is more important.
> 
> Yeah, in-situ storage would just be a QOI feature like it is for scope variables at function level.  I agree that the logical effect of scope at class level is more important.

You're talking about something like this?
class A
{
	scope Object o;
	this ()
	{
		o = new Object;
	}
}

converts to:
class A
{
	Object o;
	this ()
	{
		void* ptr = gc.allocAndIgnore(Object.classinfo.size);
		o = new(ptr) Object;
	}
	~this ()
	{
		delete o;
	}
}
« First   ‹ Prev
1 2 3