digitalmars.D - Struct copy and destruction
- Morlan (42/42) Apr 09 2011 The following code:
- spir (16/58) Apr 09 2011 This may not reflect actual processing sequence; instead be caused by ou...
- Daniel Gibson (2/66) Apr 09 2011 Or insert sleep()s
- Morlan (4/4) Apr 09 2011 The essence of my problem is why is the destructor called as a
- Mike Wey (5/9) Apr 09 2011 Because you assign a copy of s2 to s1 the struct that was originally in
- Morlan (2/2) Apr 09 2011 It sounds reasonable. But I cannot find information about this behaviour...
- Dan Olson (3/6) Apr 09 2011 I was curious too, so found in Section 7.1.5.1 the description of
- Morlan (7/9) Apr 09 2011 Section 7.1.5.1 does not apply because it concerns the case where you de...
- Dan Olson (16/27) Apr 10 2011 But if you define the more general opAssign() that takes a value for the
- Mike Wey (8/10) Apr 10 2011 Section 7.1.3.6 of TPLD talks about struct destructors, but like the
- Jason House (2/51) Apr 09 2011
- Brad Roberts (3/56) Apr 09 2011 The next version of dmd will contain a number of bug fixes for struct ct...
The following code: //*************************************************** import std.conv, std.stdio; struct Slice { int[] buff; this(size_t len) { buff = new int[len]; } this(this) { buff = buff.dup; writeln("postblit"); } } struct S { string name; Slice slc; this(string name) { writeln(name, " constructor"); this.name = name; } ~this() { writeln(name, " destructor"); } } void main() { auto s1 = S("s1"); auto s2 = S("s2"); s1 = s2; writeln("after assignment"); } //*********************************************** produces the following output: s1 constructor s2 constructor postblit s1 destructor after assignment s2 destructor s2 destructor Note the "s1 destructor" line after "postblit". I cannot find any justification for the destructor call in this place either in the TDPL book or in the Language Reference. Is this behaviour to be expected? If so I would be grateful for the proper reference. Otherwise is it a bug?
Apr 09 2011
On 04/09/2011 10:00 AM, Morlan wrote:The following code: //*************************************************** import std.conv, std.stdio; struct Slice { int[] buff; this(size_t len) { buff = new int[len]; } this(this) { buff = buff.dup; writeln("postblit"); } } struct S { string name; Slice slc; this(string name) { writeln(name, " constructor"); this.name = name; } ~this() { writeln(name, " destructor"); } } void main() { auto s1 = S("s1"); auto s2 = S("s2"); s1 = s2; writeln("after assignment"); } //*********************************************** produces the following output: s1 constructor s2 constructor postblit s1 destructor after assignment s2 destructor s2 destructor Note the "s1 destructor" line after "postblit". I cannot find any justification for the destructor call in this place either in the TDPL book or in the Language Reference. Is this behaviour to be expected? If so I would be grateful for the proper reference. Otherwise is it a bug?This may not reflect actual processing sequence; instead be caused by output, precisely the order in which strings are written on the terminal, due to queuing. I often have such messy outputs for compile / build error logging, for instance, like: error 1 error 2 compilation failed // should be last error 3 There is certainly a way to flush output streams (stdout) in D (someone knows?). Then, you can actually check. Denis -- _________________ vita es estrany spir.wikidot.com
Apr 09 2011
Am 09.04.2011 11:47, schrieb spir:On 04/09/2011 10:00 AM, Morlan wrote:Or insert sleep()sThe following code: //*************************************************** import std.conv, std.stdio; struct Slice { int[] buff; this(size_t len) { buff = new int[len]; } this(this) { buff = buff.dup; writeln("postblit"); } } struct S { string name; Slice slc; this(string name) { writeln(name, " constructor"); this.name = name; } ~this() { writeln(name, " destructor"); } } void main() { auto s1 = S("s1"); auto s2 = S("s2"); s1 = s2; writeln("after assignment"); } //*********************************************** produces the following output: s1 constructor s2 constructor postblit s1 destructor after assignment s2 destructor s2 destructor Note the "s1 destructor" line after "postblit". I cannot find any justification for the destructor call in this place either in the TDPL book or in the Language Reference. Is this behaviour to be expected? If so I would be grateful for the proper reference. Otherwise is it a bug?This may not reflect actual processing sequence; instead be caused by output, precisely the order in which strings are written on the terminal, due to queuing. I often have such messy outputs for compile / build error logging, for instance, like: error 1 error 2 compilation failed // should be last error 3 There is certainly a way to flush output streams (stdout) in D (someone knows?). Then, you can actually check. Denis
Apr 09 2011
The essence of my problem is why is the destructor called as a result of the assignment in the first place? There is no information about this behaviour in the language reference. Can anyone explain this?
Apr 09 2011
On 04/09/2011 12:42 PM, Morlan wrote:The essence of my problem is why is the destructor called as a result of the assignment in the first place? There is no information about this behaviour in the language reference. Can anyone explain this?Because you assign a copy of s2 to s1 the struct that was originally in s1 gets has it's destructor called, sine it ceases to exist. -- Mike Wey
Apr 09 2011
It sounds reasonable. But I cannot find information about this behaviour in the Language Reference or TDPL book. Can you point to a relevant source?
Apr 09 2011
Morlan <home valentimex.com> writes:It sounds reasonable. But I cannot find information about this behaviour in the Language Reference or TDPL book. Can you point to a relevant source?I was curious too, so found in Section 7.1.5.1 the description of opAssign using a swap. That explains it I think.
Apr 09 2011
Section 7.1.5.1 does not apply because it concerns the case where you define your own overload of the assignment operator. In my example the default assignment is used. The description of the default assignment is somewhat vague and does not ever mention the fact that the destructor of the target is called as its byproduct. In fact, if you add opAssign to S you will see that the destructor is not called any more. It is probably assumed that if you define your own assignment its up to you to take care of such details. This whole subject needs some reworking in the documentation.I was curious too, so found in Section 7.1.5.1 the description of opAssign using a swap. That explains it I think.
Apr 09 2011
Morlan <home valentimex.com> writes:But if you define the more general opAssign() that takes a value for the rhs instead of a ref, then the dtor is called (second example on page 257 with swap). Let me explain my thinking, maybe I was inferring too much. I noticed that the example of opAssign with the swap takes a rhs by value, so it will be a copy with its own lifecycle and must have the dtor called. The compiler could generate opAssign taking rhs by value. I think if the address of struct S to the ctor and dtor is printed (e.g.) writeln(&this, ": ", name, " destructor"); it will show that the dtor is question is called on copy. I don't think this is a bug, just compiler implementation of assignment so it can take non lvalues. -- DanSection 7.1.5.1 does not apply because it concerns the case where you define your own overload of the assignment operator. In my example the default assignment is used. The description of the default assignment is somewhat vague and does not ever mention the fact that the destructor of the target is called as its byproduct. In fact, if you add opAssign to S you will see that the destructor is not called any more. It is probably assumed that if you define your own assignment its up to you to take care of such details. This whole subject needs some reworking in the documentation.I was curious too, so found in Section 7.1.5.1 the description of opAssign using a swap. That explains it I think.
Apr 10 2011
On 04/09/2011 04:34 PM, Morlan wrote:It sounds reasonable. But I cannot find information about this behaviour in the Language Reference or TDPL book. Can you point to a relevant source?Section 7.1.3.6 of TPLD talks about struct destructors, but like the online documentation it only talks about calling the destructors when they go out of scope. But what whould you think it should do when there is no longer any reference to one of the structs? -- Mike Wey
Apr 10 2011
I agree that the output ordering does not make sense. Try altering your example slightly so the program will segfault or do some other nonsensical thing if that is truly the order of operations. Once you have that, it'd make a great bugzilla entry! It definitely looks like a bug to me. Morlan Wrote:The following code: //*************************************************** import std.conv, std.stdio; struct Slice { int[] buff; this(size_t len) { buff = new int[len]; } this(this) { buff = buff.dup; writeln("postblit"); } } struct S { string name; Slice slc; this(string name) { writeln(name, " constructor"); this.name = name; } ~this() { writeln(name, " destructor"); } } void main() { auto s1 = S("s1"); auto s2 = S("s2"); s1 = s2; writeln("after assignment"); } //*********************************************** produces the following output: s1 constructor s2 constructor postblit s1 destructor after assignment s2 destructor s2 destructor Note the "s1 destructor" line after "postblit". I cannot find any justification for the destructor call in this place either in the TDPL book or in the Language Reference. Is this behaviour to be expected? If so I would be grateful for the proper reference. Otherwise is it a bug?
Apr 09 2011
The next version of dmd will contain a number of bug fixes for struct ctor/dtor and lifetime management issues. Unless you're testing with the most current dmd code in git, I'd hold off. On 4/9/2011 8:01 AM, Jason House wrote:I agree that the output ordering does not make sense. Try altering your example slightly so the program will segfault or do some other nonsensical thing if that is truly the order of operations. Once you have that, it'd make a great bugzilla entry! It definitely looks like a bug to me. Morlan Wrote:The following code: //*************************************************** import std.conv, std.stdio; struct Slice { int[] buff; this(size_t len) { buff = new int[len]; } this(this) { buff = buff.dup; writeln("postblit"); } } struct S { string name; Slice slc; this(string name) { writeln(name, " constructor"); this.name = name; } ~this() { writeln(name, " destructor"); } } void main() { auto s1 = S("s1"); auto s2 = S("s2"); s1 = s2; writeln("after assignment"); } //*********************************************** produces the following output: s1 constructor s2 constructor postblit s1 destructor after assignment s2 destructor s2 destructor Note the "s1 destructor" line after "postblit". I cannot find any justification for the destructor call in this place either in the TDPL book or in the Language Reference. Is this behaviour to be expected? If so I would be grateful for the proper reference. Otherwise is it a bug?
Apr 09 2011