digitalmars.D - important proposal: scope keyword for class members
- John Simon (30/30) Mar 06 2009 I'd like to propose a new use for the 'scope' keyword within an aggregat...
- dsimcha (17/47) Mar 06 2009 references or pointers, but initialized directly inside the container. ...
- Christopher Wright (10/15) Mar 07 2009 scope can't use polymorphism. It's for situations in which you need your...
- John Simon (17/36) Mar 07 2009 Exactly. And on top of that, how does being 'scope' limit its polymorphi...
- Christopher Wright (3/4) Mar 07 2009 Right, but a scope variable has its type decided at compile time,
- John Simon (2/7) Mar 07 2009 True. But as long as you can still pass its reference around and have it...
- Sean Kelly (3/6) Mar 07 2009 A while back, Walter said that he planned to do exactly this. I'm not
- Sean Kelly (15/29) Mar 07 2009 Oh, I should mention that I'm not sure how the compiler would handle
- John Simon (19/51) Mar 07 2009 Wrong. The Object is constructed when it comes into scope, and destructe...
- Sean Kelly (3/23) Mar 07 2009 I'm talking about a proposed new feature, not an existing one. Please
- John Simon (4/28) Mar 08 2009 Sorry man, I thought you were disputing with me. My apologies. Let me re...
- Sean Kelly (2/31) Mar 08 2009 Ah, that makes sense. And it's an easy rule to follow.
- Bill Baxter (25/53) Mar 08 2009 ted when it leaves scope. Classes can't have an 'opAssign', instead the ...
- Andrei Alexandrescu (6/46) Mar 08 2009 To that add:
- Sergey Gromov (4/53) Mar 08 2009 Shouldn't it be sort of
- Yigal Chripun (8/36) Mar 08 2009 how can the compiler enforce such a rule?
- John Simon (3/46) Mar 09 2009 My proposal was to make scope object construction and destruction automa...
- Andrei Alexandrescu (5/19) Mar 07 2009 I'd be happier if we investigated scope in classes as an ownership
- Sean Kelly (4/23) Mar 07 2009 Yeah, in-situ storage would just be a QOI feature like it is for scope
- Christopher Wright (24/48) Mar 07 2009 You're talking about something like this?
- Sean Kelly (23/73) Mar 08 2009 For the logical aspect of scope variables, I was thinking more about
- Denis Koroskin (38/82) Mar 08 2009 I belive it could be implemented in a library. Something like this (not ...
- John Simon (3/108) Mar 09 2009 Oh man I've tried, believe me.
- Sean Kelly (4/7) Mar 09 2009 Have you tried just calling delete on the reference? That should do
- John Simon (3/11) Mar 11 2009 That would call __dtor, but wouldn't it also screw up the garbage collec...
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
== Quote from John Simon (zildjohn01 gmail.com)'s articleI'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 asreferences 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 theobject, 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
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
Christopher Wright Wrote:dsimcha wrote: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.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
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
Christopher Wright Wrote:John Simon wrote: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.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
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
Sean Kelly wrote:John Simon 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.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
Sean Kelly Wrote:Sean Kelly wrote: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.John Simon 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.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
John Simon wrote:Sean Kelly Wrote:I'm talking about a proposed new feature, not an existing one. Please take this example in context.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.
Mar 07 2009
Sean Kelly Wrote:John Simon wrote: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.Sean Kelly Wrote:I'm talking about a proposed new feature, not an existing one. Please take this example in context.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.
Mar 08 2009
John Simon wrote:Sean Kelly Wrote:Ah, that makes sense. And it's an easy rule to follow.John Simon wrote: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.Sean Kelly Wrote:I'm talking about a proposed new feature, not an existing one. Please take this example in context.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.
Mar 08 2009
On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01 gmail.com> wrote:Sean Kelly Wrote:maxJohn 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 =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.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=hrase.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=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
Bill Baxter wrote:On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01 gmail.com> wrote:Sean Kelly Wrote:John Simon wrote: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.Sean Kelly Wrote:I'm talking about a proposed new feature, not an existing one. Please take this example in context.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.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. AndreiSo 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;
Mar 08 2009
Sun, 08 Mar 2009 16:06:56 -0700, Andrei Alexandrescu wrote:Bill Baxter wrote:Shouldn't it be sort of scope(exit) (cast(TheClass)_mem.ptr).~this(); since foo may be re-assigned to something absolutely different?On Mon, Mar 9, 2009 at 7:12 AM, John Simon <zildjohn01 gmail.com> wrote:Sean Kelly Wrote:John Simon wrote: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.Sean Kelly Wrote:I'm talking about a proposed new feature, not an existing one. Please take this example in context.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.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.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;
Mar 08 2009
On 09/03/2009 00:12, John Simon wrote:Sean Kelly Wrote: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; }John Simon wrote: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.Sean Kelly Wrote:I'm talking about a proposed new feature, not an existing one. Please take this example in context.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.
Mar 08 2009
Yigal Chripun Wrote:On 09/03/2009 00:12, John Simon wrote: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.Sean Kelly Wrote: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; }John Simon wrote: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.Sean Kelly Wrote:I'm talking about a proposed new feature, not an existing one. Please take this example in context.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.
Mar 09 2009
Sean Kelly wrote:John Simon wrote: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. AndreiI'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
Andrei Alexandrescu wrote:Sean Kelly wrote: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.John Simon wrote: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.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
Sean Kelly wrote:Andrei Alexandrescu wrote: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; } }Sean Kelly wrote: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.John Simon wrote: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.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
Christopher Wright wrote:Sean Kelly wrote: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 } }Andrei Alexandrescu wrote: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; } }Sean Kelly wrote: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.John Simon wrote: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.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 08 2009
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
Denis Koroskin Wrote:On Sat, 07 Mar 2009 08:15:59 +0300, John Simon <zildjohn01 gmail.com> 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 constructorsI'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 09 2009
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 constructorsHave 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
Sean Kelly Wrote:John Simon wrote: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 createOh 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 constructorsHave 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 11 2009