www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - A question about postblit constructor

reply Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
I am trying to learn behavior of postblit constructor. Below code 
works as expected when I comment out malloc part of postblit 
constructor. It writes 4 if malloc part of postblit constructor 
is commented out. Otherwise it writes default init value of int 
which is 0. I wonder how new memory is allocated without an 
explicit malloc here. Sorry for this noob question in advance, I 
could not find any doc mentioning a similar case.

import std.stdio;
import core.stdc.stdlib;
import core.stdc.string;

struct S {
     int* vals;
     size_t length;

     this(this){
         //vals = cast(int*)malloc(length * S.sizeof);
         memcpy(vals, vals, length * S.sizeof);
         writeln("copied");
     }
}

void main()
{
     size_t len = 2;

     int* vals = cast(int*)malloc(len * S.sizeof);
     vals[0] = 4;
     vals[1] = 5;

     S s1 = S(vals, len);

     S s2 = s1;

     writeln(s2.vals[0]);

}
Nov 05 2019
parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 5 November 2019 at 08:47:05 UTC, Ferhat Kurtulmuş 
wrote:

 value of int which is 0. I wonder how new memory is allocated 
 without an explicit malloc here. Sorry for this noob question 
 in advance, I could not find any doc mentioning a similar case.
     int* vals = cast(int*)malloc(len * S.sizeof);
     vals[0] = 4;
     vals[1] = 5;

     S s1 = S(vals, len);

     S s2 = s1;

     writeln(s2.vals[0]);
writeln(s2.vals); writeln(s1.vals);
 }
https://run.dlang.io/is/D0nfeT
Nov 05 2019
parent reply Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Tuesday, 5 November 2019 at 10:13:59 UTC, Mike Parker wrote:
 On Tuesday, 5 November 2019 at 08:47:05 UTC, Ferhat Kurtulmuş 
 wrote:

 value of int which is 0. I wonder how new memory is allocated 
 without an explicit malloc here. Sorry for this noob question 
 in advance, I could not find any doc mentioning a similar case.
     int* vals = cast(int*)malloc(len * S.sizeof);
     vals[0] = 4;
     vals[1] = 5;

     S s1 = S(vals, len);

     S s2 = s1;

     writeln(s2.vals[0]);
writeln(s2.vals); writeln(s1.vals);
 }
https://run.dlang.io/is/D0nfeT
Yep, it is obvious that my code is wrong. s1 and s2 point to the same memory address. I could obtain my desired behavior with copy constructor. The documentation also say "WARNING: The postblit is considered legacy and is not recommended for new code. Code should use copy constructors defined in the previous section". struct S { int* vals; size_t length; this(ref return scope S another) { vals = cast(int*)malloc(another.length * S.sizeof); memcpy(vals, another.vals, another.length * S.sizeof); writeln("copied"); } this(int* vls, size_t len){ vals = vls; length = len; } ~this(){ free(vals); writeln("freedooom!"); } }
Nov 05 2019
parent reply Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Tuesday, 5 November 2019 at 10:31:05 UTC, Ferhat Kurtulmuş 
wrote:
 On Tuesday, 5 November 2019 at 10:13:59 UTC, Mike Parker wrote:
 [...]
Yep, it is obvious that my code is wrong. s1 and s2 point to the same memory address. I could obtain my desired behavior with copy constructor. The documentation also say "WARNING: The postblit is considered legacy and is not recommended for new code. Code should use copy constructors defined in the previous section". [...]
edit: they should be s1.vals and s2.vals
Nov 05 2019
parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 5 November 2019 at 10:32:03 UTC, Ferhat Kurtulmuş 
wrote:
 On Tuesday, 5 November 2019 at 10:31:05 UTC, Ferhat Kurtulmuş 
 wrote:
 On Tuesday, 5 November 2019 at 10:13:59 UTC, Mike Parker wrote:
 [...]
Yep, it is obvious that my code is wrong. s1 and s2 point to the same memory address. I could obtain my desired behavior with copy constructor. The documentation also say "WARNING: The postblit is considered legacy and is not recommended for new code. Code should use copy constructors defined in the previous section".
I meant the example as an answer to your statement, "I wonder how new memory is allocated without an explicit malloc here". The postblit was intended as a chance to "fixup" everything when you needed a deep copy. The new struct is initialized as a shallow copy, so when you enter into the postblit, the pointer is already pointing at the original location. Without assigning it a new malloc'ed address, your memcpy was essentially overwriting the original location with its own data.
Nov 05 2019
parent reply Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Tuesday, 5 November 2019 at 11:20:47 UTC, Mike Parker wrote:
 On Tuesday, 5 November 2019 at 10:32:03 UTC, Ferhat Kurtulmuş 
 wrote:
 On Tuesday, 5 November 2019 at 10:31:05 UTC, Ferhat Kurtulmuş 
 wrote:
[...]
I meant the example as an answer to your statement, "I wonder how new memory is allocated without an explicit malloc here". The postblit was intended as a chance to "fixup" everything when you needed a deep copy. The new struct is initialized as a shallow copy, so when you enter into the postblit, the pointer is already pointing at the original location. Without assigning it a new malloc'ed address, your memcpy was essentially overwriting the original location with its own data.
What I need was a deep copy, and I thought I could have done it using postblit constructor. Thanks for clarification.
Nov 05 2019
parent reply Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Tuesday, 5 November 2019 at 12:06:44 UTC, Ferhat Kurtulmuş 
wrote:
 On Tuesday, 5 November 2019 at 11:20:47 UTC, Mike Parker wrote:
 On Tuesday, 5 November 2019 at 10:32:03 UTC, Ferhat Kurtulmuş 
 wrote:
[...]
I meant the example as an answer to your statement, "I wonder how new memory is allocated without an explicit malloc here". The postblit was intended as a chance to "fixup" everything when you needed a deep copy. The new struct is initialized as a shallow copy, so when you enter into the postblit, the pointer is already pointing at the original location. Without assigning it a new malloc'ed address, your memcpy was essentially overwriting the original location with its own data.
What I need was a deep copy, and I thought I could have done it using postblit constructor. Thanks for clarification.
I think copy constructor is less confusing though.
Nov 05 2019
next sibling parent Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Tuesday, 5 November 2019 at 12:09:15 UTC, Ferhat Kurtulmuş 
wrote:
 On Tuesday, 5 November 2019 at 12:06:44 UTC, Ferhat Kurtulmuş 
 wrote:
 On Tuesday, 5 November 2019 at 11:20:47 UTC, Mike Parker wrote:
 On Tuesday, 5 November 2019 at 10:32:03 UTC, Ferhat Kurtulmuş 
 wrote:
[...]
I meant the example as an answer to your statement, "I wonder how new memory is allocated without an explicit malloc here". The postblit was intended as a chance to "fixup" everything when you needed a deep copy. The new struct is initialized as a shallow copy, so when you enter into the postblit, the pointer is already pointing at the original location. Without assigning it a new malloc'ed address, your memcpy was essentially overwriting the original location with its own data.
What I need was a deep copy, and I thought I could have done it using postblit constructor. Thanks for clarification.
I think copy constructor is less confusing though.
Oh I see I have to do this to make a deep copy using postblit constructor: this(this){ int* _vals = cast(int*)malloc(length * S.sizeof); foreach(i; 0..length) _vals[i] = vals[i]; vals = _vals; writeln("copied"); }
Nov 05 2019
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, November 5, 2019 5:09:15 AM MST Ferhat Kurtulmuş via 
Digitalmars-d-learn wrote:
 On Tuesday, 5 November 2019 at 12:06:44 UTC, Ferhat Kurtulmuş

 wrote:
 On Tuesday, 5 November 2019 at 11:20:47 UTC, Mike Parker wrote:
 On Tuesday, 5 November 2019 at 10:32:03 UTC, Ferhat Kurtulmuş

 wrote:
[...]
I meant the example as an answer to your statement, "I wonder how new memory is allocated without an explicit malloc here". The postblit was intended as a chance to "fixup" everything when you needed a deep copy. The new struct is initialized as a shallow copy, so when you enter into the postblit, the pointer is already pointing at the original location. Without assigning it a new malloc'ed address, your memcpy was essentially overwriting the original location with its own data.
What I need was a deep copy, and I thought I could have done it using postblit constructor. Thanks for clarification.
I think copy constructor is less confusing though.
It's pretty simple really. The compiler takes care of copying everything and then the postblit constructor only has to worry about stuff where a deep copy is needed. It's called a postblit, because it's run after the bit blitting that's used to do a shallow copy. If an object has no postblit constructor and has no member variables with postblit constructors, then all a copy does is blit. When a D object with a postblit constructor is run, you get three steps: 1. A shallow copy of the object is made by bit blitting it. 2. Then the postblit constructors (if any) of the member variables are run. 3. Then the postblit constructor for the object is run. The result is that in your average postblit constructor, you only have to deal with a portion of the member variables, whereas for a copy constructor, you're forced to deal with _every_ member. In principle, postblit constructors are actually a great idea, because they make it so that you only have to worry about the members that actually need deep copies. Where they fall apart is with modifiers like const. Because a postblit constructor does a shallow copy and then you modify it, it requires modification, which just doesn't work with const. So, while postblit constructors were a good idea with D1 (which was a much simpler language), with D2 (which added const as we know it), they've been far more of a problem, which is why there was a DIP not long ago to add copy constructors to D to replace postblit constructors. Because of how long postblit constructors have been around, they may never actually be deprecated, but ideally, newer code would use copy constructors, and over time, postblit constructors wouldn't be used anymore. Fortunately, D2 has fantastic metaprogramming, so it's actually possible to write copy constructors that copy all of the members without having to explicitly copy all of them by name. So, the main benefit of the postblit constructor (not needing to explicitly copy everything) isn't as much of an improvement as it originally was. DIP: https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1018.md It looks like the release that added copy constructors to the compiler was 2.086 back in May: https://dlang.org/changelog/2.086.0.html https://dlang.org/changelog/2.086.0.html#copy_constructor So, while it's definitely useful for you to understand postblit constructors, any code you're writing now should probably use copy constructors. So, if you have a good understanding of copy constructors and are having trouble with postblit constructors, presumably, that's an improvement for you, though you may still need to deal with postblit constructors in existing code that other people have written. - Jonathan M Davis
Nov 06 2019
parent Ferhat =?UTF-8?B?S3VydHVsbXXFnw==?= <aferust gmail.com> writes:
On Wednesday, 6 November 2019 at 09:19:04 UTC, Jonathan M Davis 
wrote:
 DIP: 
 https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1018.md

 It looks like the release that added copy constructors to the 
 compiler was 2.086 back in May:

 https://dlang.org/changelog/2.086.0.html 
 https://dlang.org/changelog/2.086.0.html#copy_constructor
I've been playing around with d for less than a year. I didn't know that copy ctors were included in the language until checking the docs for postblit ctors (It is a relatively new thing, so I just missed it). Sorry, I keep picking my words wrongly. By saying "less confusing" for copy ctors, I meant more readable code for me (a newbie) in terms of syntax. This piece of code was confusing for me (from Ali's book: this(this){grades = grades.dup;}). Postblit ctor has a special scope. With the help of this thread, I understood postblit ctors. Thank you.
Nov 06 2019