www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - move object from heap to stack

reply "Namespace" <rswhite4 googlemail.com> writes:
Is that possible?
I can initialize an object with scope or, in feature, with 
scoped, directly on the stack but is it also possible to move an 
existing object from the heap to the stack?
Sep 19 2012
next sibling parent reply "Namespace" <rswhite4 googlemail.com> writes:
On Wednesday, 19 September 2012 at 13:32:42 UTC, Namespace wrote:
 Is that possible?
 I can initialize an object with scope or, in feature, with 
 scoped, directly on the stack but is it also possible to move 
 an existing object from the heap to the stack?

I tried this: http://dpaste.dzfl.pl/2955ff41 But as you can see in line 25/26, after I destroyed the heap object, the stack object seems to be corrupted.
Sep 19 2012
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/20/2012 03:37 AM, monarch_dodra wrote:
 On Thursday, 20 September 2012 at 10:20:24 UTC, Johannes Pfau wrote:
 http://dlang.org/struct.html
 D classes do not really have copy constructors. You could assume that
 if a constructor has only one argument and it's of the same type as
 the class, it's a copy constructor.

True... the term "copy constructor" is not 100% accurate, it is just "a constructor that takes the same type" A a = new A(); //Creates a new A A b = a; //Binds to the existing A A c = new A(a); //Constructs a new A, form an old A.

A polymorphic member function would be better for when A is just a base class, i.e. when we don't know its actual type (the virtual constructor idiom): void foo(Animal a) { Animal c = a.dup; // can be any Animal // ... } Ali
Sep 20 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Wed, 19 Sep 2012 15:45:21 +0200, Namespace <rswhite4 googlemail.com>  
wrote:

 On Wednesday, 19 September 2012 at 13:32:42 UTC, Namespace wrote:
 Is that possible?
 I can initialize an object with scope or, in feature, with scoped,  
 directly on the stack but is it also possible to move an existing  
 object from the heap to the stack?

I tried this: http://dpaste.dzfl.pl/2955ff41 But as you can see in line 25/26, after I destroyed the heap object, the stack object seems to be corrupted.

The problem here is that A.sizeof returns the size of the reference, not the instance. Instead you should use __traits(classInstanceSize, A). Also, do have a look at the internals of std.typecons.scoped, it likely contains some good ideas. -- Simen
Sep 19 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
Thanks, I will use __traits(classInstanceSize, A);
But it does not work either.
Sep 19 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 19 September 2012 at 14:16:31 UTC, Namespace wrote:
 Thanks, I will use __traits(classInstanceSize, A);
 But it does not work either.

This is because classes are already pointers. when you write "&a", you are getting the address of pointer itself. So when you memcopy, don't use "&a", but use "cast(void*)a". What you were currently doing was memcopying the "pointer to a" into your chunk. From there, since you were casting to A*, everything worked and was legal, but you still only had 1 class intance. Your destroying that instance made your ap crash. this works: http://dpaste.dzfl.pl/8ba1f457
Sep 19 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Wednesday, 19 September 2012 at 17:08:28 UTC, monarch_dodra 
wrote:
 On Wednesday, 19 September 2012 at 14:16:31 UTC, Namespace 
 wrote:
 Thanks, I will use __traits(classInstanceSize, A);
 But it does not work either.

This is because classes are already pointers. when you write "&a", you are getting the address of pointer itself. So when you memcopy, don't use "&a", but use "cast(void*)a". What you were currently doing was memcopying the "pointer to a" into your chunk. From there, since you were casting to A*, everything worked and was legal, but you still only had 1 class intance. Your destroying that instance made your ap crash. this works: http://dpaste.dzfl.pl/8ba1f457

I forget this. Thank you! IMO something like this should exist in phobos. A more flexible version of scoped also.
Sep 19 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
Result:
http://dpaste.dzfl.pl/24988d8f
Sep 19 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 19 September 2012 at 19:24:34 UTC, Namespace wrote:
 Result:
 http://dpaste.dzfl.pl/24988d8f

I like it, but how can something placed on the stack be reference counted? The "chunk" is also placed on the stack, so it doesn't really make sense to me: When the object goes out of scope, the chunk goes, regardless of ref count. I think you should ditch the whole ref count thing. You'll also have to be aware of the dangers of putting a class instance on the stack: main() { A a; { stacked_obj!A so; so.gen; a = so.get; } a.print(); //<-- undefined, with or without reference count } IE: Big red comment you should NEVER extract an instance from the stack.
Sep 19 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
I count it to destroy it if it isn't used anymore.
I can remove this behaviour but IMO with this you haven't 
unnecessary use of stack memory. Am I wrong?
Furthermore, if I copy the stack object e.g. if I pass it as 
value parameter to another function/class, I can store it as long 
as it's needed. Maybe that should be the task of the heap, but 
I'm not sure.
Sep 19 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 19 September 2012 at 21:36:56 UTC, Namespace wrote:
 I count it to destroy it if it isn't used anymore.

of scope, it gets destroyed, 100% of the time. The "chunk" disappears, along with any object inside it. When that happens, it doesn't matter what the count is, whatever is inside chunk MUST be destroyed.
 I can remove this behaviour but IMO with this you haven't 
 unnecessary use of stack memory. Am I wrong?

The reference count doesn't change that. When you pass by value, a new chunk is created an copied. Your postblit is weird: this(this) { this._use_counter++; this._use_counter = _use_counter; } The way postblit works is that first, the "other" is memcopyed onto this. Then, postblit modifies the current object. "this._use_counter = _use_counter;" makes no sense. One more thing: You can't memcopy a class from one place to another, because you don't know if it has a CC or not. You *could* swap the class, which would be safe for the class itself, but not for anything else referencing the class.
 Furthermore, if I copy the stack object e.g. if I pass it as 
 value parameter to another function/class, I can store it as 
 long as it's needed. Maybe that should be the task of the heap, 
 but I'm not sure.

Anyways, I re-read scoped's implementation, and classes ARE allocated on the stack. Further more, they can emplace their new object. They can also copy an existing class into them... provided the class gives access to CC. It is not a move, but, IMO, a move would be unsafe anyways. import std.typecons; class A { this(){} this(int){} this(A){} } void main() { auto sa = scoped!A(new A()); auto sb = scoped!A(5); } I'm not entirely.
Sep 19 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
You're right.
This is my next try: http://dpaste.dzfl.pl/02b32d33
Do you have anything else to say? :)
Sep 20 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 20 September 2012 at 08:10:36 UTC, Namespace wrote:
 You're right.
 This is my next try: http://dpaste.dzfl.pl/02b32d33
 Do you have anything else to say? :)

I think it looks good, but I'm unsure about "move", or allowing pass by value: Classes can't be memcopied the way structs can: Class may have default constructors, or copy constructors, which would be totally ignored by a memcopy. For example, if your class had a "RefCounted" member, its postblit would never be called, and it would then be "over destroyed": -------- class A { RefCounted!int rf; this() { rf.RefCounted.ensureInitialized(); rf = 5; } } void main() { A a = new A(); OnStack!A osa = OnStack!A.move(a); } -------- core.exception.InvalidMemoryOperationError -------- Here, osa makes a binary copy of the RefCounted, so the count stays at 1. Then, when osa is destroyed, it deletes the RefCounted's store. However, when a is destroyed, it still has a pointer to the (deleted) store, creating the Error... I think "move" has to go, because classes are just not moveable. pass by value can stay, if and only if, T gives a copy constructor. Or NO constructors (in which case a memcopy should be the same as a default copy).
Sep 20 2012
prev sibling next sibling parent Johannes Pfau <nospam example.com> writes:
Am Thu, 20 Sep 2012 10:11:37 +0200
schrieb "Namespace" <rswhite4 googlemail.com>:

 You're right.
 This is my next try: http://dpaste.dzfl.pl/02b32d33
 Do you have anything else to say? :)

I think it shouldn't be possible to copy an OnStack struct. The destructor is run for every copy, but the constructor was run only once. So if a class keeps external references (e.g. file handles) and closes those handles in the destructor, making a copy would result in the destructor running twice and therefore closing the same handle twice. I think the usage of a OnStack struct must be quite limited to be safe: { auto a = OnStack!(A)(); //allocate in scope a.doSomething(); //Calling members is fine //a.get and a must not escape this scope (I guess we can't guarantee //that without compiler help?) //passing a.get to a function is valid, as long as the function //doesn't keep a reference (the function parameter must be marked //with scope) someMethod(a.get); //As a shouldn't be copied, an OnStack can't be passed to another //function. //no copies for OnStack allowed //destroy at end of scope } Those rules are pretty strict, but maybe that's the only way to have a safe OnStack. Maybe I forgot some detail and those rules aren't even strict enough.
Sep 20 2012
prev sibling next sibling parent Johannes Pfau <nospam example.com> writes:
Am Thu, 20 Sep 2012 12:06:28 +0200
schrieb "monarch_dodra" <monarchdodra gmail.com>:

 On Thursday, 20 September 2012 at 08:10:36 UTC, Namespace wrote:
 You're right.
 This is my next try: http://dpaste.dzfl.pl/02b32d33
 Do you have anything else to say? :)

I think it looks good, but I'm unsure about "move", or allowing pass by value: Classes can't be memcopied the way structs can: Class may have default constructors, or copy constructors, which would be totally ignored by a memcopy. For example, if your class had a "RefCounted" member, its postblit would never be called, and it would then be "over destroyed": -------- class A { RefCounted!int rf; this() { rf.RefCounted.ensureInitialized(); rf = 5; } } void main() { A a = new A(); OnStack!A osa = OnStack!A.move(a); } -------- core.exception.InvalidMemoryOperationError -------- Here, osa makes a binary copy of the RefCounted, so the count stays at 1. Then, when osa is destroyed, it deletes the RefCounted's store. However, when a is destroyed, it still has a pointer to the (deleted) store, creating the Error... I think "move" has to go, because classes are just not moveable. pass by value can stay, if and only if, T gives a copy constructor. Or NO constructors (in which case a memcopy should be the same as a default copy).

http://dlang.org/struct.html D classes do not really have copy constructors. You could assume that if a constructor has only one argument and it's of the same type as the class, it's a copy constructor. But that's a pure convention then, nothing than could be enforced in any way. Copying classes is dangerous, and problems can also happen without constructors: ---------- class A { void* handle; ~this() { if(handle) free(handle); } } { auto a = OnStack!A(); a.handle = someHandle; auto b = a; //copy } //end of scope, someHandle is freed twice ---------- This can even happen if the class does not keep any reference types. The example would also be valid with a file handle (uint).
Sep 20 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 20 September 2012 at 10:20:24 UTC, Johannes Pfau 
wrote:
 http://dlang.org/struct.html
 D classes do not really have copy constructors. You could 
 assume that
 if a constructor has only one argument and it's of the same 
 type as the class, it's a copy constructor.

True... the term "copy constructor" is not 100% accurate, it is just "a constructor that takes the same type" A a = new A(); //Creates a new A A b = a; //Binds to the existing A A c = new A(a); //Constructs a new A, form an old A.
 But that's a pure convention then,
 nothing than could be enforced in any way. Copying classes is
 dangerous, and problems can also happen without constructors:
 [SNIP]

Yes, that is a good point. I could find ways to make it crash even with a POD class, so I guess we could relax the condition to "don't ever memcpy a class: They aren't designed for that. Bad! Bad! Don't do it!" I'm learning from this thread.
Sep 20 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Namespace:

 is it also possible to move an existing object from the heap to 
 the stack?

What's the point of doing it? Bye, bearophile
Sep 20 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
So I should disable the copy ctor ( disable this(this)) and drop 
move, right?
As you can See if I pass a OnStack to function/class i'm using 
.get all the Time. The only other method IMO would be to pass it 
as OnStack by value and Store it in the other class also as 
OnStack, right?

 bearophiole: just to Know if something lile this is possible.
Sep 20 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 20 September 2012 at 12:32:03 UTC, Namespace wrote:
 So I should disable the copy ctor ( disable this(this)) and 
 drop move, right?

:/
 As you can See if I pass a OnStack to function/class i'm using 
 .get all the Time.

as a T implicitly. Again, this is what scoped!T does.
 The only other method IMO would be to pass it as OnStack by 
 value and Store it in the other class also as OnStack, right?

Yes, but that would require a copy contructor "this(this)", which we have actually ruled out. You can pass the T itself thanks to the implicit cast to T, but you can't pass an actual OnStack object.
Sep 20 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Thursday, 20 September 2012 at 14:02:01 UTC, monarch_dodra 
wrote:
 On Thursday, 20 September 2012 at 12:32:03 UTC, Namespace wrote:
 So I should disable the copy ctor ( disable this(this)) and 
 drop move, right?

re-implemented :/
 As you can See if I pass a OnStack to function/class i'm using 
 .get all the Time.

cast as a T implicitly. Again, this is what scoped!T does.
 The only other method IMO would be to pass it as OnStack by 
 value and Store it in the other class also as OnStack, right?

Yes, but that would require a copy contructor "this(this)", which we have actually ruled out. You can pass the T itself thanks to the implicit cast to T, but you can't pass an actual OnStack object.

http://dpaste.dzfl.pl/edit/361a54eb With [code] disable this(this); [/code] this don't work (as expected). But with the copy ctor but without ".get" you earn a segmentation fault (as you can see). So you can disable the copy ctor but without using .get you're not get what you want. Even with scoped, but scoped's getter name is too long.
Sep 20 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 20 September 2012 at 14:09:29 UTC, Namespace wrote:

 http://dpaste.dzfl.pl/edit/361a54eb
 With
 [code]
  disable this(this);
 [/code]
 this don't work (as expected). But with the copy ctor but 
 without ".get" you earn a segmentation fault (as you can see). 
 So you can disable the copy ctor but without using .get you're 
 not get what you want. Even with scoped, but scoped's getter 
 name is too long.

Wrong link.
Sep 20 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Thursday, 20 September 2012 at 14:15:23 UTC, monarch_dodra 
wrote:
 On Thursday, 20 September 2012 at 14:09:29 UTC, Namespace wrote:

 http://dpaste.dzfl.pl/edit/361a54eb
 With
 [code]
  disable this(this);
 [/code]
 this don't work (as expected). But with the copy ctor but 
 without ".get" you earn a segmentation fault (as you can see). 
 So you can disable the copy ctor but without using .get you're 
 not get what you want. Even with scoped, but scoped's getter 
 name is too long.

Wrong link.

Try this: http://dpaste.dzfl.pl/9247af54
Sep 20 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 20 September 2012 at 15:04:48 UTC, Namespace wrote:
 On Thursday, 20 September 2012 at 14:15:23 UTC, monarch_dodra 
 wrote:
 On Thursday, 20 September 2012 at 14:09:29 UTC, Namespace 
 wrote:

 http://dpaste.dzfl.pl/edit/361a54eb
 With
 [code]
  disable this(this);
 [/code]
 this don't work (as expected). But with the copy ctor but 
 without ".get" you earn a segmentation fault (as you can 
 see). So you can disable the copy ctor but without using .get 
 you're not get what you want. Even with scoped, but scoped's 
 getter name is too long.

Wrong link.

Try this: http://dpaste.dzfl.pl/9247af54

What did you expect? You are passing your OnStack by value. In this particular case, you are over destroying your OnStack!B, which in turn over destroyes his _a. Reactivate the disable, and you'll see the problem blatantly: http://dpaste.dzfl.pl/9e873033 /home/c192/c104.d(102): Error: struct c104.OnStack!(A).OnStack is not copyable because it is annotated with disable The "problem" with implicit cast is that *sometimes* you think it does it, but in fact, it doesn't. Here, it didn't. The opCall expects Arg... so if you give it an OnStack, it accepts an OnStack. If you want it to take a T, you have to specify it, either in the template, or with get, or with a cast. Here is a cast version, with this(this) disabled: http://dpaste.dzfl.pl/837f44a9
Sep 20 2012
prev sibling parent "Namespace" <rswhite4 googlemail.com> writes:
I expect nothing, my only intention was to show you, that this 
cannot work. But that it works with an explicit cast is very 
impressive. I thought the alias this does it automatically.
I'm learning too. ;)
Sep 20 2012