digitalmars.D - scope class members -> in-situ
- Andrei Alexandrescu (22/22) Oct 02 2009 I think this has been discussed in this group already.
- Bill Baxter (21/42) Oct 02 2009 ans
- Andrei Alexandrescu (8/59) Oct 02 2009 Yah, the idea was to make it not rebindable. Initially I even thought of...
- bearophile (4/7) Oct 02 2009 I hope Andrei's book will explain how (and where) to use placement const...
- bearophile (6/17) Oct 02 2009 I have recently discussed about this. One idea is to keep the compiler s...
- Daniel Keep (9/10) Oct 02 2009 *Think*?!
- Andrei Alexandrescu (3/24) Oct 02 2009 Well "think" does qualify doesn't it :o).
- Tom S (94/122) Oct 02 2009 I think it should be done in userland, not built-in. Here's a
- Andrei Alexandrescu (4/6) Oct 02 2009 [awesome code snipped]
- Andrei Alexandrescu (4/6) Oct 02 2009 [awesome code snipped]
- Christian Kamm (6/8) Oct 02 2009 I agree and also had a go at it a few months back:
- Denis Koroskin (39/48) Oct 03 2009 Yes, it is possible, but their use is dangerous without non-nullable
- Yigal Chripun (26/80) Oct 03 2009 I think scope should be completely removed from the language. it seems
- bearophile (4/5) Oct 02 2009 It may be good for the compiler to show a warning (that explains exactly...
- BCS (4/9) Oct 03 2009 I think the same device used for the base class constructor could be use...
I think this has been discussed in this group already. An object storing another object needs two allocations: class A { ... } class B { A a; this() { a = new A; } } auto b = new B; // two allocations I'm thinking of using "scope" in this situation to imply in-situ storage: class B { scope A a; this() { a = new A; } } Now the A member actually lies inside of B - no more indirection. That means the constructor needs special scrutiny, in particular a cannot be null because that wouldn't make much sense. What do you think? Andrei
Oct 02 2009
On Fri, Oct 2, 2009 at 8:33 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I think this has been discussed in this group already. An object storing another object needs two allocations: class A { ... } class B { =A0 A a; =A0 this() { =A0 =A0 =A0a =3D new A; =A0 } } auto b =3D new B; // two allocations I'm thinking of using "scope" in this situation to imply in-situ storage: class B { =A0 scope A a; =A0 this() { =A0 =A0 =A0a =3D new A; =A0 } } Now the A member actually lies inside of B - no more indirection. That me=ansthe constructor needs special scrutiny, in particular a cannot be null because that wouldn't make much sense. What do you think?I think it would be nice, but there are enough issues that I'm not convinced that saving a few allocations is worth it. Mainly what happens if you do someB.a =3D someOtherA later on? The answer to that might be different than how you treat someB.a =3D new A= (). The latter could be converted into a placement new. But the former has to still point to the original someOtherA to maintain proper reference semantics. You can sidestep these issues by saying that a scope A in a class is not rebindable. But that no doubt cuts out some useful cases. Another option would be to make "scope A" reserve space for both a pointer to an A and the memory for it. Then the someB.a is just a regular A reference that can be rebound like any other, or made null if desired. It would just leave orphaned memory sitting there if rebound to point to some other A. Or it could use placement construction if you assign a new A to it. (but not if you say someB.a =3D new DerivedFromA()). --bb
Oct 02 2009
Bill Baxter wrote:On Fri, Oct 2, 2009 at 8:33 AM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Yah, the idea was to make it not rebindable. Initially I even thought of using "final" instead of "scope".I think this has been discussed in this group already. An object storing another object needs two allocations: class A { ... } class B { A a; this() { a = new A; } } auto b = new B; // two allocations I'm thinking of using "scope" in this situation to imply in-situ storage: class B { scope A a; this() { a = new A; } } Now the A member actually lies inside of B - no more indirection. That means the constructor needs special scrutiny, in particular a cannot be null because that wouldn't make much sense. What do you think?I think it would be nice, but there are enough issues that I'm not convinced that saving a few allocations is worth it. Mainly what happens if you do someB.a = someOtherA later on? The answer to that might be different than how you treat someB.a = new A(). The latter could be converted into a placement new. But the former has to still point to the original someOtherA to maintain proper reference semantics. You can sidestep these issues by saying that a scope A in a class is not rebindable. But that no doubt cuts out some useful cases.Another option would be to make "scope A" reserve space for both a pointer to an A and the memory for it. Then the someB.a is just a regular A reference that can be rebound like any other, or made null if desired. It would just leave orphaned memory sitting there if rebound to point to some other A. Or it could use placement construction if you assign a new A to it. (but not if you say someB.a = new DerivedFromA()).Having a reference plus in-situ storage also crossed my mind, but I think it would serve corner cases that could best be served by either using two allocations, or having the user herself define one reference + one scope object. Andrei
Oct 02 2009
Bill Baxter:Or it could use placement construction if you assign a new A to it. (but not if you say someB.a = new DerivedFromA()).I hope Andrei's book will explain how (and where) to use placement construction for classes in D. Bye, bearophile
Oct 02 2009
Andrei Alexandrescu:class B { scope A a; this() { a = new A; } } Now the A member actually lies inside of B - no more indirection. That means the constructor needs special scrutiny, in particular a cannot be null because that wouldn't make much sense. What do you think?I have recently discussed about this. One idea is to keep the compiler simpler, keeping such scoped class as possible similar to normal class. So you keep the indirection. So B keeps inside itself the memory needed to keep an A plus a reference to A itself. This wastes a little memory for the reference and keeps the indirection, but allows to keep the semantics of A almost unchanged, because you can always do reassign "a" to another instance of A (an instance that can allocated on the heap too). In such situation the main and maybe only thing you have to keep care of is the deallocation of "a", that has to not happen (well, you can call its destructor, but you can't actually free its memory), because even if the GC has declared it as dead (because of a "delete" or because "a" has being reassigned to something else), it can't be deallocated until the instance of B is deallocated. There are other ways to implement this, but I think they require more special-casing and more changes to the frontend and probably a little more complexity. Bye, bearophile
Oct 02 2009
Andrei Alexandrescu wrote:I think this has been discussed in this group already.*Think*?! http://www.digitalmars.com/d/archives/digitalmars/D/scope_inline_optimizations_scoped_attributes_95025.html http://www.digitalmars.com/d/archives/digitalmars/D/D2_s_feature_set_91823.html http://www.digitalmars.com/d/archives/digitalmars/D/important_proposal_scope_keyword_for_class_members_85524.html http://www.digitalmars.com/d/archives/digitalmars/D/learn/How_to_write_a_proper_class_destructor_6113.html#N6120 http://www.digitalmars.com/d/archives/digitalmars/D/38329.html And I'm certain I've missed quite a few. I know history is cyclical, but this is getting ridiculous.
Oct 02 2009
Daniel Keep wrote:Andrei Alexandrescu wrote:Well "think" does qualify doesn't it :o). AndreiI think this has been discussed in this group already.*Think*?! http://www.digitalmars.com/d/archives/digitalmars/D/scope_inline_optimizations_scoped_attributes_95025.html http://www.digitalmars.com/d/archives/digitalmars/D/D2_s_feature_set_91823.html http://www.digitalmars.com/d/archives/digitalmars/D/important_proposal_scope_keyword_for_class_members_85524.html http://www.digitalmars.com/d/archives/digitalmars/D/learn/How_to_write_a_proper_class_destructor_6113.html#N6120 http://www.digitalmars.com/d/archives/digitalmars/D/38329.html And I'm certain I've missed quite a few. I know history is cyclical, but this is getting ridiculous.
Oct 02 2009
Andrei Alexandrescu wrote:I think this has been discussed in this group already. An object storing another object needs two allocations: class A { ... } class B { A a; this() { a = new A; } } auto b = new B; // two allocations I'm thinking of using "scope" in this situation to imply in-situ storage: class B { scope A a; this() { a = new A; } } Now the A member actually lies inside of B - no more indirection. That means the constructor needs special scrutiny, in particular a cannot be null because that wouldn't make much sense. What do you think?I think it should be done in userland, not built-in. Here's a proof-of-concept implementation: ---- import std.stdio; string scopeInstance(string type, string name) { return ` private byte[__traits(classInstanceSize, `~type~`)] _scopeInstance__`~name~`; static this() { const int off = _scopeInstance__`~name~`.offsetof; const int len = __traits(classInstanceSize, `~type~`); typeof(this).classinfo.init[off .. off + len] = `~type~`.classinfo.init; } `~type~` `~name~`() { return cast(`~type~`)_scopeInstance__`~name~`.ptr; } void `~name~`(`~type~` f) { _scopeInstance__`~name~`[] = (cast(byte*)f)[0.._scopeInstance__`~name~`.sizeof]; }`; } class Foo { int x; float y; string zomg = "zomg"; this(int x, float y) { writeln("making a Foo"); this.x = x; this.y = y; } ~this() { writeln("death-tracting a Foo"); } } class Bar { string x; this (string x) { writeln("making a Bar"); this.x = x; } } class Baz { mixin(scopeInstance("Foo", "foo")); mixin(scopeInstance("Bar", "bar")); this() { writeln("making a Baz"); foo.__ctor(1, 3.14f); bar.__ctor("ohai"); } ~this() { writeln("death-tracting a Baz"); foo.__dtor(); } } void main() { scope b = new Baz; writeln(b.foo.x, " ", b.foo.y, " ", b.foo.zomg); writeln(b.bar.x); writeln("Foo size: ", __traits(classInstanceSize, Foo)); writeln("Bar size: ", __traits(classInstanceSize, Bar)); writeln("Baz size: ", __traits(classInstanceSize, Baz)); } ---- Result: ---- making a Baz making a Foo making a Bar 1 3.14 zomg ohai Foo size: 24 Bar size: 16 Baz size: 48 death-tracting a Baz death-tracting a Foo ---- Now for a brief summary: * I couldn't get hold of the initializer of a class at compile-time. I've tried using symbol.mangleof ~ "6__initZ", but DMD told me it could not be an initializer for a static array. This forced me to initialize the 'inline' / 'scope' class instances in a static ctor. I'm not sure if this is legal - can ClassInfo ever wind up to live in ROM? Perhaps __traits(classInitializer, _) could be added. * The long proposed __ident extension would allow turning the string mixin into a regular template mixin. * __ctor should be standardized (or is it?) I'm not very good at D2, so perhaps there are better ways to implement it by now. Here's a similar proposal in pseudo-D2-code: http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=85684 -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Oct 02 2009
Tom S wrote:I think it should be done in userland, not built-in. Here's a proof-of-concept implementation:[awesome code snipped] I am struck with awe. Thanks. Andrei
Oct 02 2009
Tom S wrote:I think it should be done in userland, not built-in. Here's a proof-of-concept implementation:[awesome code snipped] I am struck with awe. Thanks. Andrei
Oct 02 2009
Tom S wrote:I think it should be done in userland, not built-in. Here's a proof-of-concept implementation:I agree and also had a go at it a few months back: http://www.digitalmars.com/d/archives/digitalmars/D/scope_as_template_struct_82104.html What about alignment issues though? I think we need to force that byte array with the class data to have the same alignment restrictions as the class data.
Oct 02 2009
On Sat, 03 Oct 2009 10:52:09 +0400, Christian Kamm <kamm-incasoftware removethis.de> wrote:Tom S wrote:Yes, it is possible, but their use is dangerous without non-nullable *value* types, because user is not forced to initialize it in ctor. I believe compiler should complain unless user explicitly assigns a value to such variable: class Foo { Scope!(Bar) bar; this() { //bar.construct(args); - a no-go, use of an unassigned variable. // How can compiler distinguish between bar.constract(args), // which is allowed to be called on non-constructed object, and // bar.doSomething(), which is not? // Besides, it hides possibly existing Bar.construct method. // bar = new Bar(args); - desirable syntax, but impossible w/o compiler help bar = Scope!Bar(args); // explicitly initialized bar.doSomething(); // okay } } I think we should either drop "scope" feature altogether in favor of library solution, or extend it to scope classes. The former is not possible unless there is a way to implement scope(exit), scope(success) and scope(failure) in library, which I am not aware of. That's why I prefer built-in scope class members ATM. Be it implemented in a compiler, should it be rebindable or not? Andrei mentioned that it's probably not, but then it creates an inconsistency with local scope variable (those are rebindable). Another difference is that currently the following code is allowed: Foo createFoo() { return new FooDerivative(); } scope Foo foo = createFoo(); // allocates on heap anyway, which might be confusing Shouldn't compiler complain in case scope variable is heap-allocated? Shall we unify the two concepts (local and class member scope variables)? If yes, then how?I think it should be done in userland, not built-in. Here's a proof-of-concept implementation:I agree and also had a go at it a few months back: http://www.digitalmars.com/d/archives/digitalmars/D/scope_as_template_struct_82104.html What about alignment issues though? I think we need to force that byte array with the class data to have the same alignment restrictions as the class data.
Oct 03 2009
On 03/10/2009 11:59, Denis Koroskin wrote:On Sat, 03 Oct 2009 10:52:09 +0400, Christian Kamm <kamm-incasoftware removethis.de> wrote:I think scope should be completely removed from the language. it seems to me like the register keyword in C. this aspect of memory management would be better off as an optimization by the compiler when applicable instead of user specified. consider: class B {...} class A { // desirable syntax: // const B obj; NonRebindable!B obj; // assume language support instead of template this (args) { obj = new B(args); } } since obj is non-rebindable the compiler can optimize this without requiring the user to specify "scope". local variables in a function would have similar semantics: void foo() { NonRebindable!B = new B; .. } final classes and immutables can also scoped. unless the user wants to explicitly handle memory with C malloc, I think the best thing would be to separate lifetime management from memory management. the programmer defines the lifetime of the data while the language/compiler/runtime handle all other aspects like choosing stack vs. heap, allocating and de-allocating (GC)Tom S wrote:Yes, it is possible, but their use is dangerous without non-nullable *value* types, because user is not forced to initialize it in ctor. I believe compiler should complain unless user explicitly assigns a value to such variable: class Foo { Scope!(Bar) bar; this() { //bar.construct(args); - a no-go, use of an unassigned variable. // How can compiler distinguish between bar.constract(args), // which is allowed to be called on non-constructed object, and // bar.doSomething(), which is not? // Besides, it hides possibly existing Bar.construct method. // bar = new Bar(args); - desirable syntax, but impossible w/o compiler help bar = Scope!Bar(args); // explicitly initialized bar.doSomething(); // okay } } I think we should either drop "scope" feature altogether in favor of library solution, or extend it to scope classes. The former is not possible unless there is a way to implement scope(exit), scope(success) and scope(failure) in library, which I am not aware of. That's why I prefer built-in scope class members ATM. Be it implemented in a compiler, should it be rebindable or not? Andrei mentioned that it's probably not, but then it creates an inconsistency with local scope variable (those are rebindable). Another difference is that currently the following code is allowed: Foo createFoo() { return new FooDerivative(); } scope Foo foo = createFoo(); // allocates on heap anyway, which might be confusing Shouldn't compiler complain in case scope variable is heap-allocated? Shall we unify the two concepts (local and class member scope variables)? If yes, then how?I think it should be done in userland, not built-in. Here's a proof-of-concept implementation:I agree and also had a go at it a few months back: http://www.digitalmars.com/d/archives/digitalmars/D/scope_as_template_struct_82104.html What about alignment issues though? I think we need to force that byte array with the class data to have the same alignment restrictions as the class data.
Oct 03 2009
Andrei Alexandrescu:I'm thinking of using "scope" in this situation to imply in-situ storage:<It may be good for the compiler to show a warning (that explains exactly why) where the compiler can't scope a class marked as scope (both in a function or in a method). Bye, bearophile
Oct 02 2009
Hello Andrei,I think this has been discussed in this group already.[...]That means the constructor needs special scrutiny, in particular a cannot be null because that wouldn't make much sense.I think the same device used for the base class constructor could be used. What syntax it would use could be an issue but...
Oct 03 2009