www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Struct beeing moved around

reply Benjamin Thaut <code benjamin-thaut.de> writes:
The following program:

import std.stdio;

struct test {
   this(this){
     writefln("postblit");
   }

   int foo;

   this(int i){
     foo = i;
     writefln("%x",&foo);
   }

   ~this(){
     writefln("%x",&foo);
   }
}

void main(string[] args){
      test t = test(5);
}


Gives me this output on dmd 2.052:
18fe58
18fe54

Is this a bug in 2.052? (Doesn't happen with 2.053)
Why did the location of the struct change?
Is there any way to get informed about a struct beeing moved?
Is there a way to prevent it?
-- 
Kind Regards
Benjamin Thaut
May 20 2011
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
On May 20, 2011, at 5:14 AM, Benjamin Thaut wrote:

 The following program:
=20
 import std.stdio;
=20
 struct test {
  this(this){
    writefln("postblit");
  }
=20
  int foo;
=20
  this(int i){
    foo =3D i;
    writefln("%x",&foo);
  }
=20
  ~this(){
    writefln("%x",&foo);
  }
 }
=20
 void main(string[] args){
     test t =3D test(5);
 }
=20
=20
 Gives me this output on dmd 2.052:
 18fe58
 18fe54
=20
 Is this a bug in 2.052? (Doesn't happen with 2.053)
 Why did the location of the struct change?
 Is there any way to get informed about a struct beeing moved?
 Is there a way to prevent it?

In main above you're declaring a new struct variable t which is = default-constructed, then a temporary is created and initialized to 5, = and then the temporary is copied onto t. It does seem like a postblit = should probably occur in this scenario though.
May 20 2011
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-05-20 10:30:33 -0400, Sean Kelly <sean invisibleduck.org> said:

 On May 20, 2011, at 5:14 AM, Benjamin Thaut wrote:
 
 The following program:
 
 import std.stdio;
 
 struct test {
 this(this){
 writefln("postblit");
 }
 
 int foo;
 
 this(int i){
 foo = i;
 writefln("%x",&foo);
 }
 
 ~this(){
 writefln("%x",&foo);
 }
 }
 
 void main(string[] args){
 test t = test(5);
 }
 
 
 Gives me this output on dmd 2.052:
 18fe58
 18fe54
 
 Is this a bug in 2.052? (Doesn't happen with 2.053)
 Why did the location of the struct change?
 Is there any way to get informed about a struct beeing moved?
 Is there a way to prevent it?

In main above you're declaring a new struct variable t which is default-constructed, then a temporary is created and initialized to 5, and then the temporary is copied onto t. It does seem like a postblit should probably occur in this scenario though.

Postblit is a post-copy constructor, not a post-move one. There is no such thing as a post-move constructor in D: structs are assumed to be movable. If the compiler was constructing the struct in place (instead of moving it) it'd be even better, but that's more a question of optimization than semantics. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
May 20 2011
next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 20.05.2011 19:28, schrieb Michel Fortin:
 On 2011-05-20 10:30:33 -0400, Sean Kelly <sean invisibleduck.org> said:

 On May 20, 2011, at 5:14 AM, Benjamin Thaut wrote:

 The following program:

 import std.stdio;

 struct test {
 this(this){
 writefln("postblit");
 }

 int foo;

 this(int i){
 foo = i;
 writefln("%x",&foo);
 }

 ~this(){
 writefln("%x",&foo);
 }
 }

 void main(string[] args){
 test t = test(5);
 }


 Gives me this output on dmd 2.052:
 18fe58
 18fe54

 Is this a bug in 2.052? (Doesn't happen with 2.053)
 Why did the location of the struct change?
 Is there any way to get informed about a struct beeing moved?
 Is there a way to prevent it?

In main above you're declaring a new struct variable t which is default-constructed, then a temporary is created and initialized to 5, and then the temporary is copied onto t. It does seem like a postblit should probably occur in this scenario though.

Postblit is a post-copy constructor, not a post-move one. There is no such thing as a post-move constructor in D: structs are assumed to be movable. If the compiler was constructing the struct in place (instead of moving it) it'd be even better, but that's more a question of optimization than semantics.

What if I need a value type that may be copied, but may not be moved? -- Kind Regards Benjamin Thaut
May 20 2011
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-05-20 13:36:58 -0400, Benjamin Thaut <code benjamin-thaut.de> said:

 What if I need a value type that may be copied, but may not be moved?

I don't think that's possible. Why would you want that? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
May 20 2011
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 20.05.2011 19:51, schrieb Michel Fortin:
 On 2011-05-20 13:36:58 -0400, Benjamin Thaut <code benjamin-thaut.de> said:

 What if I need a value type that may be copied, but may not be moved?

I don't think that's possible. Why would you want that?

you can not do the pointer guessing game the current D GC does. So you have to create a reference value type, that registers itself with the GC when it is constructed and deregisteres itself on destruction. This reference type is only to be used inside functions for references that are constructed on the stack. class foo { ... } void bar(){ foo f = new foo(); } would become void bar(){ Ref!foo f = Ref!foo(new foo()); } where Ref is: struct Ref(T){ T m_Ref; alias m_Ref this; this(){ GC.addRoot(&m_Ref); } ~this(){ GC.removeRoot(&m_Ref); } } If there is no garantuee that a struct stays on the same memory location within 1 Stack frame, you pretty much can not implement a other GC unless you hack into the compiler. -- Kind Regards Benjamin Thaut
May 20 2011
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2011-05-20 14:46:03 -0400, Benjamin Thaut <code benjamin-thaut.de> said:

 Am 20.05.2011 19:51, schrieb Michel Fortin:
 On 2011-05-20 13:36:58 -0400, Benjamin Thaut <code benjamin-thaut.de> said:
 
 What if I need a value type that may be copied, but may not be moved?

I don't think that's possible. Why would you want that?

[...] If there is no garantuee that a struct stays on the same memory location within 1 Stack frame, you pretty much can not implement a other GC unless you hack into the compiler.

But... is that a real problem or just something you've come up with to argue that a move constructor can be useful? I ask because it looks pretty synthetic: if you're going to call something alike GC.addRoot/GC.removeRoot upon construction and destruction of this smart pointer you could as well implement standard reference counting. And reference counting does not need a move constructor since moving doesn't change the reference count. But this is an interesting topic. I'm currently hacking the compiler to support Objective-C objects. As part of this I want the memory to be managed automatically, which isn't so easy because Objective-C objects have to live in a separate heap. So I'll need to add support for an external memory management system and make it work seamlessly with the D GC. I already know how I'm going to implement it, and it won't require anything like a move constructor. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
May 20 2011
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 20.05.2011 22:57, schrieb Michel Fortin:
 On 2011-05-20 14:46:03 -0400, Benjamin Thaut <code benjamin-thaut.de> said:

 Am 20.05.2011 19:51, schrieb Michel Fortin:
 On 2011-05-20 13:36:58 -0400, Benjamin Thaut <code benjamin-thaut.de>
 said:

 What if I need a value type that may be copied, but may not be moved?

I don't think that's possible. Why would you want that?

[...] If there is no garantuee that a struct stays on the same memory location within 1 Stack frame, you pretty much can not implement a other GC unless you hack into the compiler.

But... is that a real problem or just something you've come up with to argue that a move constructor can be useful? I ask because it looks pretty synthetic: if you're going to call something alike GC.addRoot/GC.removeRoot upon construction and destruction of this smart pointer you could as well implement standard reference counting. And reference counting does not need a move constructor since moving doesn't change the reference count. But this is an interesting topic. I'm currently hacking the compiler to support Objective-C objects. As part of this I want the memory to be managed automatically, which isn't so easy because Objective-C objects have to live in a separate heap. So I'll need to add support for an external memory management system and make it work seamlessly with the D GC. I already know how I'm going to implement it, and it won't require anything like a move constructor.

for the Lisp VM I'm implementing in D 2.0. A Baker GC needs to know exactly where the pointers are located, because it needs to change the pointers during collection. I'm only doing the GC.addRoot/GC.removeRoot for refernces that are allocated on that Stack. Not for every reference. And reference couting is not sufficient, because it does not break cycles. As the GC needs to change the pointers, I would need a move constructor, so I can update the pointers memory location. I'm curious, are there plans that D is going to use something that is not a Mark & Sweep? -- Kind Regards Benjamin Thaut
May 21 2011
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2011-05-21 05:44:58 -0400, Benjamin Thaut <code benjamin-thaut.de> said:

 Am 20.05.2011 22:57, schrieb Michel Fortin:
 On 2011-05-20 14:46:03 -0400, Benjamin Thaut <code benjamin-thaut.de> said:
 
 Am 20.05.2011 19:51, schrieb Michel Fortin:
 On 2011-05-20 13:36:58 -0400, Benjamin Thaut <code benjamin-thaut.de>
 said:
 
 What if I need a value type that may be copied, but may not be moved?

I don't think that's possible. Why would you want that?

[...] If there is no garantuee that a struct stays on the same memory location within 1 Stack frame, you pretty much can not implement a other GC unless you hack into the compiler.

But... is that a real problem or just something you've come up with to argue that a move constructor can be useful? I ask because it looks pretty synthetic: if you're going to call something alike GC.addRoot/GC.removeRoot upon construction and destruction of this smart pointer you could as well implement standard reference counting. And reference counting does not need a move constructor since moving doesn't change the reference count. But this is an interesting topic. I'm currently hacking the compiler to support Objective-C objects. As part of this I want the memory to be managed automatically, which isn't so easy because Objective-C objects have to live in a separate heap. So I'll need to add support for an external memory management system and make it work seamlessly with the D GC. I already know how I'm going to implement it, and it won't require anything like a move constructor.

for the Lisp VM I'm implementing in D 2.0. A Baker GC needs to know exactly where the pointers are located, because it needs to change the pointers during collection. I'm only doing the GC.addRoot/GC.removeRoot for refernces that are allocated on that Stack. Not for every reference. And reference couting is not sufficient, because it does not break cycles. As the GC needs to change the pointers, I would need a move constructor, so I can update the pointers memory location. I'm curious, are there plans that D is going to use something that is not a Mark & Sweep?

Not that I know of. There has been some work to make scanning certain parts of the memory precise to reduce false pointer issues, but not for the stack though. <http://d.puremagic.com/issues/show_bug.cgi?id=3463> I don't see many solutions to your problem in the current state of things. Assuming the language doesn't change to accommodate your need you could add one level of indirection for your stack references and update that indirection instead. But that's probably not very satisfactory. I think you should at least fill an enhancement request in bugzilla for move-constructors (or at least the ability to disable moves and force it to do construct/assign/destruct instead) with an explanation of your use case. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
May 22 2011
prev sibling parent reply KennyTM~ <kennytm gmail.com> writes:
On May 21, 11 01:28, Michel Fortin wrote:
 Postblit is a post-copy constructor, not a post-move one. There is no
 such thing as a post-move constructor in D: structs are assumed to be
 movable.

The assumption is better written in the spec. There's no single mention of the word "move" in struct.html.
May 20 2011
parent Benjamin Thaut <code benjamin-thaut.de> writes:
Am 20.05.2011 20:33, schrieb KennyTM~:
 On May 21, 11 01:28, Michel Fortin wrote:
 Postblit is a post-copy constructor, not a post-move one. There is no
 such thing as a post-move constructor in D: structs are assumed to be
 movable.

The assumption is better written in the spec. There's no single mention of the word "move" in struct.html.

"A struct is defined to not have an identity; that is, the implementation is free to make bit copies of the struct as convenient." -- Kind Regards Benjamin Thaut
May 20 2011
prev sibling next sibling parent so <so so.so> writes:
On Fri, 20 May 2011 17:30:33 +0300, Sean Kelly <sean invisibleduck.org>  
wrote:

 In main above you're declaring a new struct variable t which is  
 default-constructed, then a temporary is created and initialized to 5,  
 and then the temporary is copied onto t.  It does seem like a postblit  
 should probably occur in this scenario though.

I am confused. There shouldn't be any copying, it is construction.
May 20 2011
prev sibling next sibling parent Sean Kelly <sean invisibleduck.org> writes:
On May 20, 2011, at 8:43 AM, so wrote:

 On Fri, 20 May 2011 17:30:33 +0300, Sean Kelly =

=20
 In main above you're declaring a new struct variable t which is =


and then the temporary is copied onto t. It does seem like a postblit = should probably occur in this scenario though.
=20
 I am confused.
 There shouldn't be any copying, it is construction.

If you do the following: MyStruct s; A new instance of MyStruct is be created and default-initialized. = Changing this to: MyStruct s =3D MyStruct(5); Is really just shorthand for: MyStruct s; s =3D MyStruct(5); The compiler can translate this into a single construction (it does in = C++), but I don't know that it's required to.=
May 20 2011
prev sibling next sibling parent Sean Kelly <sean invisibleduck.org> writes:
On May 20, 2011, at 10:28 AM, Michel Fortin wrote:

 On 2011-05-20 10:30:33 -0400, Sean Kelly <sean invisibleduck.org> =

=20
 In main above you're declaring a new struct variable t which is
 default-constructed, then a temporary is created and initialized to =


 and then the temporary is copied onto t.  It does seem like a =


 should probably occur in this scenario though.

Postblit is a post-copy constructor, not a post-move one. There is no =

movable. Ah right. So internal pointers don't work in structs.=
May 20 2011
prev sibling next sibling parent so <so so.so> writes:
On Fri, 20 May 2011 20:42:26 +0300, Sean Kelly <sean invisibleduck.org>  
wrote:

 The compiler can translate this into a single construction (it does in  
 C++), but I don't know that it's required to.

If not, i don't know any other use for constructors, you can do the almost same thing with a function. struct A { } A construct() { } void main() { A a = construct(); }
May 20 2011
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
 On Fri, 20 May 2011 20:42:26 +0300, Sean Kelly <sean invisibleduck.org>
 
 wrote:
 The compiler can translate this into a single construction (it does in
 C++), but I don't know that it's required to.

If not, i don't know any other use for constructors, you can do the almost same thing with a function. struct A { } A construct() { } void main() { A a = construct(); }

That wouldn't work with A a(args, to, constructor); Now, I personally never use that syntax and always use auto a = A(args, to, constructor); but you can't do the first form without a constructor, and even in the second form, you're making it clear that you're spefically constructing a value of that type as opposed to using a some random function which happens to produce that type. But ultimately, constructors are just special functions anyway. - Jonathan M Davis
May 20 2011
prev sibling next sibling parent so <so so.so> writes:
On Fri, 20 May 2011 21:44:19 +0300, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 That wouldn't work with

 A a(args, to, constructor);

 Now, I personally never use that syntax and always use

 auto a = A(args, to, constructor);

 but you can't do the first form without a constructor, and even in the  
 second
 form, you're making it clear that you're spefically constructing a value  
 of
 that type as opposed to using a some random function which happens to  
 produce
 that type. But ultimately, constructors are just special functions  
 anyway.

 - Jonathan M Davis

Wait a minute, i thought "A a(args, to, constructor)" wasn't allowed in D. And "test t(5);" gives an error.
May 20 2011
prev sibling next sibling parent kenji hara <k.hara.pg gmail.com> writes:
--000e0cd5194cd43b2804a3b9f7b6
Content-Type: text/plain; charset=ISO-8859-1

2011/05/20 21:15 "Benjamin Thaut" <code benjamin-thaut.de>:
 Is this a bug in 2.052? (Doesn't happen with 2.053)

Yes, this is a bug of 2.052. Temporary object destruction problem was fixed in 2.053. Kenji Hara --000e0cd5194cd43b2804a3b9f7b6 Content-Type: text/html; charset=ISO-8859-1 <p><br> 2011/05/20 21:15 &quot;Benjamin Thaut&quot; &lt;<a href="mailto:code benjamin-thaut.de">code benjamin-thaut.de</a>&gt;:<br> &gt; Is this a bug in 2.052? (Doesn&#39;t happen with 2.053)</p> <p>Yes, this is a bug of 2.052. Temporary object destruction problem was fixed in 2.053.</p> <p>Kenji Hara</p> --000e0cd5194cd43b2804a3b9f7b6--
May 20 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-05-20 11:52, so wrote:
 On Fri, 20 May 2011 21:44:19 +0300, Jonathan M Davis <jmdavisProg gmx.com>
 
 wrote:
 That wouldn't work with
 
 A a(args, to, constructor);
 
 Now, I personally never use that syntax and always use
 
 auto a = A(args, to, constructor);
 
 but you can't do the first form without a constructor, and even in the
 second
 form, you're making it clear that you're spefically constructing a value
 of
 that type as opposed to using a some random function which happens to
 produce
 that type. But ultimately, constructors are just special functions
 anyway.
 
 - Jonathan M Davis

Wait a minute, i thought "A a(args, to, constructor)" wasn't allowed in D. And "test t(5);" gives an error.

Hmm. I was sure that it was, but I just tried it, and it won't compile. I'm confused now. I never use that style though - even with no arguments to the constructor - so that the type's static opCall will be used instead of its init value if it has one. e.g. auto a = A(); instead of A a; So, it's not something that I would ever have tried anyway. But I guess that it was either changed ot some point, or I misremembered about it being legal. - Jonathan M Davis
May 20 2011
prev sibling parent Brad Roberts <braddr puremagic.com> writes:
On Sat, 21 May 2011, kenji hara wrote:

 2011/05/20 21:15 "Benjamin Thaut" <code benjamin-thaut.de>:
 Is this a bug in 2.052? (Doesn't happen with 2.053)

Yes, this is a bug of 2.052. Temporary object destruction problem was fixed in 2.053. Kenji Hara

Caution.. SOME of the temp object destruction problems were fixed, but not all. At a minimum, the intersection with exception handling parts are not yet complete. Later, Brad
May 20 2011