www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - important proposal: scope keyword for class members

reply John Simon <zildjohn01 gmail.com> writes:
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.
Mar 06 2009
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== 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.
Mar 06 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
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);
Mar 07 2009
parent reply John Simon <zildjohn01 gmail.com> writes:
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.
Mar 07 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
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.
Mar 07 2009
parent John Simon <zildjohn01 gmail.com> writes:
Christopher Wright Wrote:

 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.
True. But as long as you can still pass its reference around and have it behave correctly, I don't see how that's an issue.
Mar 07 2009
prev sibling next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
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.
Mar 07 2009
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
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.
Mar 07 2009
parent reply John Simon <zildjohn01 gmail.com> writes:
Sean Kelly 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.
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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error. In C++ it's equivalent to: class Object{void *classInfo; ...}; class A : Object {}; void normalDClass() { Object *o; o = new A(); // OK! o = new B(); // OK! } void scopeDClass() { Object o; // initialized when entering function o = A(); // usually calls operator=(A &), i.e. opAssign(A &). // This is forbidden in D since A& is implicitly converible to Object. // Therefore a compile time error happens. // also, o is destructed here } The existing rules would work very well. The only thing you need to be careful of, is not to pass the object to someone who will keep the reference around.
Mar 07 2009
parent reply Sean Kelly <sean invisibleduck.org> writes:
John Simon wrote:
 Sean Kelly Wrote:
 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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
I'm talking about a proposed new feature, not an existing one. Please take this example in context.
Mar 07 2009
parent reply John Simon <zildjohn01 gmail.com> writes:
Sean Kelly Wrote:

 John Simon wrote:
 Sean Kelly Wrote:
 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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
I'm talking about a proposed new feature, not an existing one. Please take this example in context.
Sorry man, I thought you were disputing with me. My apologies. Let me rephrase. I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it. So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.
Mar 08 2009
next sibling parent Sean Kelly <sean invisibleduck.org> writes:
John Simon wrote:
 Sean Kelly Wrote:
 
 John Simon wrote:
 Sean Kelly Wrote:
 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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
I'm talking about a proposed new feature, not an existing one. Please take this example in context.
Sorry man, I thought you were disputing with me. My apologies. Let me rephrase. I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it. So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.
Ah, that makes sense. And it's an easy rule to follow.
Mar 08 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01 gmail.com> wrote:
 Sean Kelly Wrote:

 John Simon wrote:
 Sean Kelly Wrote:
 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 {
 =A0 =A0 =A0this( bool b ) {
 =A0 =A0 =A0 =A0 =A0if( b ) o =3D new A;
 =A0 =A0 =A0 =A0 =A0else =A0 =A0o =3D new B;
 =A0 =A0 =A0}
 =A0 =A0 =A0scope 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.
Wrong. The Object is constructed when it comes into scope, and destruc=
ted when it leaves scope. Classes can't have an 'opAssign', instead the ref= erence is reassigned. Since the reference is invariant/immutable here, this= throws a compile time error.
 I'm talking about a proposed new feature, not an existing one. =A0Please
 take this example in context.
Sorry man, I thought you were disputing with me. My apologies. Let me rep=
hrase.
 I believe that 'scope' declared objects shouldn't allow an assignment of =
a derived type. Reasoning being that there wouldn't be enough stack space a= llocated for it.
 So in your example above, Object could only recieve a 'new Object', and n=
othing further down in the hierarchy. I'm pretty sure that assigning to a scope class on the stack is not done by copying the memory. I'm pretty sure what happens is basically something like follows. This scope statement: scope foo =3D new TheClass; Basically becomes this: static ubyte[sizeof_TheClass] _mem; auto foo =3D new(_mem.ptr) TheClass; Where new(addr) indicates a "placement new". So the memory for the initial instance is allocated on the stack, yes, but other than that foo act just like any other class pointer. In particular if you say foo=3Dotherfoo; you're just reassigning the pointer, not actually doing anything with the object allocated on the stack. It just becomes unreferenced junk. And before Walter says it -- I know, I should use obj2asm and look at the assembly to see for sure. --bb
Mar 08 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01 gmail.com> wrote:
 Sean Kelly Wrote:

 John Simon wrote:
 Sean Kelly Wrote:
 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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
I'm talking about a proposed new feature, not an existing one. Please take this example in context.
Sorry man, I thought you were disputing with me. My apologies. Let me rephrase. I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it.
 So in your example above, Object could only recieve a 'new Object', and
nothing further down in the hierarchy.
I'm pretty sure that assigning to a scope class on the stack is not done by copying the memory. I'm pretty sure what happens is basically something like follows. This scope statement: scope foo = new TheClass; Basically becomes this: static ubyte[sizeof_TheClass] _mem; auto foo = new(_mem.ptr) TheClass;
To that add: scope(exit) foo.~this(); I don't think there's a syntax for destructor invocation. Never liked scope that much. Extremely dangerous for such a cute syntax. Andrei
Mar 08 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Sun, 08 Mar 2009 16:06:56 -0700, Andrei Alexandrescu wrote:

 Bill Baxter wrote:
 On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01 gmail.com> wrote:
 Sean Kelly Wrote:

 John Simon wrote:
 Sean Kelly Wrote:
 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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
I'm talking about a proposed new feature, not an existing one. Please take this example in context.
Sorry man, I thought you were disputing with me. My apologies. Let me rephrase. I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it.
 So in your example above, Object could only recieve a 'new Object', and
nothing further down in the hierarchy.
I'm pretty sure that assigning to a scope class on the stack is not done by copying the memory. I'm pretty sure what happens is basically something like follows. This scope statement: scope foo = new TheClass; Basically becomes this: static ubyte[sizeof_TheClass] _mem; auto foo = new(_mem.ptr) TheClass;
To that add: scope(exit) foo.~this(); I don't think there's a syntax for destructor invocation. Never liked scope that much. Extremely dangerous for such a cute syntax.
Shouldn't it be sort of scope(exit) (cast(TheClass)_mem.ptr).~this(); since foo may be re-assigned to something absolutely different?
Mar 08 2009
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
On 09/03/2009 00:12, John Simon wrote:
 Sean Kelly Wrote:

 John Simon wrote:
 Sean Kelly Wrote:
 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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
I'm talking about a proposed new feature, not an existing one. Please take this example in context.
Sorry man, I thought you were disputing with me. My apologies. Let me rephrase. I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it. So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.
how can the compiler enforce such a rule? class A { byte[16]; } class B : A { byte[32]; } class C { this(A a) { obj = a; } scope A obj; }
Mar 08 2009
parent John Simon <zildjohn01 gmail.com> writes:
Yigal Chripun Wrote:

 On 09/03/2009 00:12, John Simon wrote:
 Sean Kelly Wrote:

 John Simon wrote:
 Sean Kelly Wrote:
 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.
Wrong. The Object is constructed when it comes into scope, and destructed when it leaves scope. Classes can't have an 'opAssign', instead the reference is reassigned. Since the reference is invariant/immutable here, this throws a compile time error.
I'm talking about a proposed new feature, not an existing one. Please take this example in context.
Sorry man, I thought you were disputing with me. My apologies. Let me rephrase. I believe that 'scope' declared objects shouldn't allow an assignment of a derived type. Reasoning being that there wouldn't be enough stack space allocated for it. So in your example above, Object could only recieve a 'new Object', and nothing further down in the hierarchy.
how can the compiler enforce such a rule? class A { byte[16]; } class B : A { byte[32]; } class C { this(A a) { obj = a; } scope A obj; }
My proposal was to make scope object construction and destruction automatic and implicit, meaning any assignment would be illegal, besides an inline construction by the definition of the same type as the scope variable. However even if that restriction wasn't there, the runtime could restrict it the same way it does dynamic casting. I see no point to doing it this second way though.
Mar 09 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Mar 07 2009
parent reply Sean Kelly <sean invisibleduck.org> writes:
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.
Mar 07 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
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; } }
Mar 07 2009
parent Sean Kelly <sean invisibleduck.org> writes:
Christopher Wright wrote:
 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; } }
For the logical aspect of scope variables, I was thinking more about possibly restricting whether they are allowed to escape from the scope in which they are declared, etc. The QOI feature would be to make the related allocations occur within the enclosing scope. So, for example: class C { byte[16] x; } Object fn() { scope C var = new C; // allocated on the stack return var; // possibly flagged as an error by the compiler } class D { scope C var; this() { var = new C; // in-place constructed within D } C get() { return var; // possibly flagged as an error } }
Mar 08 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sat, 07 Mar 2009 08:15:59 +0300, John Simon <zildjohn01 gmail.com> 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.

 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.
I belive it could be implemented in a library. Something like this (not tested): struct Scope(T) { static assert(is(T : class)); private ubyte[SizeOf!(T)] _data = T.init[]; private bool _isConstructed = false; T value() { return cast(T)_data.ptr; } alias this value; void construct(Params...)(Params params) // perhaps, could be 'this' { assert(!_isConstructed); value.ctor(params); _isConstructed = true; } } Usage: class Foo { this(int i) { this.i = i; } int i; void doSomething() { } } class Bar { Scope!(Foo) foo; this() { // optional: foo.construct(42); // different syntax is also possible // same as foo = new Foo(42); Foo f = foo; // implicit cast foo.doSomething(); int i = foo.i; } }
Mar 08 2009
parent reply John Simon <zildjohn01 gmail.com> writes:
Denis Koroskin Wrote:

 On Sat, 07 Mar 2009 08:15:59 +0300, John Simon <zildjohn01 gmail.com> 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.

 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.
I belive it could be implemented in a library. Something like this (not tested): struct Scope(T) { static assert(is(T : class)); private ubyte[SizeOf!(T)] _data = T.init[]; private bool _isConstructed = false; T value() { return cast(T)_data.ptr; } alias this value; void construct(Params...)(Params params) // perhaps, could be 'this' { assert(!_isConstructed); value.ctor(params); _isConstructed = true; } } Usage: class Foo { this(int i) { this.i = i; } int i; void doSomething() { } } class Bar { Scope!(Foo) foo; this() { // optional: foo.construct(42); // different syntax is also possible // same as foo = new Foo(42); Foo f = foo; // implicit cast foo.doSomething(); int i = foo.i; } }
Oh man I've tried, believe me. I couldn't figure out how to call the destructors in-place. Also, structs can't have parameter-less constructors
Mar 09 2009
parent reply Sean Kelly <sean invisibleduck.org> writes:
John Simon wrote:
 
 Oh man I've tried, believe me.
 I couldn't figure out how to call the destructors in-place. Also, structs
can't have parameter-less constructors
Have you tried just calling delete on the reference? That should do what you want. In D2 it certainly will at any rate... I haven't looked at the D1 lib source in a while.
Mar 09 2009
parent John Simon <zildjohn01 gmail.com> writes:
Sean Kelly Wrote:

 John Simon wrote:
 
 Oh man I've tried, believe me.
 I couldn't figure out how to call the destructors in-place. Also, structs
can't have parameter-less constructors
Have you tried just calling delete on the reference? That should do what you want. In D2 it certainly will at any rate... I haven't looked at the D1 lib source in a while.
That would call __dtor, but wouldn't it also screw up the garbage collector or allocator? Since the bytes would be located at offset 0 in the struct, wouldn't it try to free the same memory twice? Also I'm guessing there's no viable solution to the constructor problem, besides a mandatory member function. Which kills a lot of the elegance this type of construct would create
Mar 11 2009