Re: A question about postblit constructor

2019-11-06 Thread Ferhat Kurtulmuş via Digitalmars-d-learn
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.


Re: A question about postblit constructor

2019-11-06 Thread Jonathan M Davis via Digitalmars-d-learn
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






Re: A question about postblit constructor

2019-11-05 Thread Ferhat Kurtulmuş via Digitalmars-d-learn
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");
}


Re: A question about postblit constructor

2019-11-05 Thread Ferhat Kurtulmuş via Digitalmars-d-learn
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.


Re: A question about postblit constructor

2019-11-05 Thread Ferhat Kurtulmuş via Digitalmars-d-learn

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.


Re: A question about postblit constructor

2019-11-05 Thread Mike Parker via Digitalmars-d-learn
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.


Re: A question about postblit constructor

2019-11-05 Thread Ferhat Kurtulmuş via Digitalmars-d-learn

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!");
}
}


Re: A question about postblit constructor

2019-11-05 Thread Ferhat Kurtulmuş via Digitalmars-d-learn
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


Re: A question about postblit constructor

2019-11-05 Thread Mike Parker via Digitalmars-d-learn
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


A question about postblit constructor

2019-11-05 Thread Ferhat Kurtulmuş via Digitalmars-d-learn
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]);

}


Re: Postblit constructor

2018-03-02 Thread Simen Kjærås via Digitalmars-d-learn

On Wednesday, 28 February 2018 at 18:23:04 UTC, Jiyan wrote:
The nodes are only allocated over malloc(). So i have to take 
care of the initialisation myself.
The problem is, that i found out by debugging, that it seems 
that when i call val.opAssign(op) in constructNodeFrom(), there 
isn't any postblit constructor called in there, the struct 
seems just to get copied by a memcpy, can that be? Or is it a 
bug? Shouldn't the postblit constructor get called there?


I'm not sure what you're expecting. The postblit is called when 
an operation results in there being two copies of something:


import std.stdio;

struct S {
this(this) {
writeln("Postblit!");
}
}

S test() {
return S.init;
}

void test2(S s) {}

unittest {
S s1;
writeln("Copy construction:");
S s2 = s1; // There's now two copies of s, so postblit is 
called.

writeln("Copy assignment:");
s2 = s1;   // Again there are two copies, so postblit again.
writeln("opAssign:");
s2.opAssign(s1); // And one more time.
writeln("Function call:");
test2(s1); // And finally.
writeln("Move construction:");
s2 = test(); // This time, only one copy exists, so postblit 
is not called.

}

In each of these cases the values are first copied using memcpy 
or the like, then postblit is called in the first four cases. In 
the last case the value is moved from test() to s2, and so 
there's only ever one copy, and no postblit is necessary.


For more details, we can add a destructor:

import std.stdio;

struct S {
int n;
this(this) {
writeln("Postblit!");
}
~this() {
writeln("~this");
}
}

S test() {
return S.init;
}

void test2(S s) {}

unittest {
S s1;
writeln("Copy construction:");
S s2 = s1;
writeln("Copy assignment:");
s2 = s1;
writeln("opAssign:");
s2.opAssign(s1);
writeln("Function call:");
test2(s1);
writeln("Move construction:");
s2 = test();
writeln("Done.");
}

And the output:

Copy construction:
Postblit!  // Called from s2.

Copy assignment:
Postblit!  // Called from s2.
~this  // Temporary copy of s2.

opAssign:
Postblit!  // Called from s2.
~this  // Temporary copy of s2.

Function call:
Postblit!  // Called from temporary created to be 
function argument.
~this  // The same temporary. Destroyed when test2() 
returns.


Move construction:
~this  // Temporary copy of s2.

Done.
~this  // s2 going out of scope.
~this  // s1 going out of scope.

It might seem weird that s2's destructor is called after the 
postblit is called. This is because a temporary copy of s2 is 
created, so that if the postblit throws, the original values are 
copied back, and everything's still in a valid state.


As we can see from this annotated output, a call resulting in a 
postblit (e.g. s2 = s;) is lowered to essentially this code:


S temp; // Create temporary.
memcpy(temp, s2, S.sizeof); // Fill temporary with values from s2.
memcpy(s2, s, S.sizeof); // Copy data from s to s2.
try {
s2.__postblit(); // Call postblit.
temp.__dtor(); // Destroy temporary.
} catch (Throwable) {
memcpy(s2, temp, S.sizeof); // If postblit failed, copy 
temporary back.

}

--
  Simen


Re: Postblit constructor

2018-03-02 Thread Eduard Staniloiu via Digitalmars-d-learn

On Wednesday, 28 February 2018 at 18:27:49 UTC, Jiyan wrote:

On Wednesday, 28 February 2018 at 18:23:04 UTC, Jiyan wrote:

Hey,
i thought i had understood postblit, but in my Code the 
following is happening (simplified):


struct C
{
this(this){/*Do sth*/}
list!C;

void opAssign(const C c)
{
 "Postlbit from C called".writeln;
 // Do sth
}
}


Sry of course it is

"Postlbit from C called".writeln; in this(this)
and the c from opAssign should get constructed via postblit. 
Doesnt it?


I'm not sure I understood your question, so please let me know if 
this clears things out or not.


The parameter of opAssign will be constructed through a postblit 
call, but you need to explicitly assign what you want from the 
parameter inside the instance of _this_ object.


Re: Postblit constructor

2018-02-28 Thread Jiyan via Digitalmars-d-learn

On Wednesday, 28 February 2018 at 18:23:04 UTC, Jiyan wrote:

Hey,
i thought i had understood postblit, but in my Code the 
following is happening (simplified):


struct C
{
this(this){/*Do sth*/}
list!C;

void opAssign(const C c)
{
 "Postlbit from C called".writeln;
 // Do sth
}
}


Sry of course it is

"Postlbit from C called".writeln; in this(this)
and the c from opAssign should get constructed via postblit. 
Doesnt it?


Postblit constructor

2018-02-28 Thread Jiyan via Digitalmars-d-learn

Hey,
i thought i had understood postblit, but in my Code the following 
is happening (simplified):


struct C
{
this(this){/*Do sth*/}
list!C;

void opAssign(const C c)
{
 "Postlbit from C called".writeln;
 // Do sth
}
}

struct list(T)
{
node* head;
node* last;
size_t size;

struct node
{
T val;
node* next;
node* prev;

void deleteNode()
{
static if(!isPointer!T){
static if(__traits(compiles,val.__xdtor))val.__xdtor;
}
}

void constructNodeFrom(ref const T op)
{
 static if(isPointer!T)
 {
  val = cast(T) op;
 }
 else
 {
  val.opAssign(op);
 }
}

~this(){this.erase}
 /// Methods Add, Remove, erase are implemented here ...
}

The nodes are only allocated over malloc(). So i have to take 
care of the initioalisation myself.
The problem is, that i found out by debugging, that it seems that 
when i call val.opAssign(op) in constructNodeFrom(), there isnt 
any postblit constructor called in there, the struct seems just 
to get copied by a memcpy, can that be? Or is it a bug? Shouldnt 
the postblit constructor get called there?


Greetings


Re: destructor, postblit constructor --- force calling always

2014-03-31 Thread Benjamin Thaut

Am 31.03.2014 08:06, schrieb monarch_dodra:

On Monday, 31 March 2014 at 01:03:22 UTC, Carl Sturtivant wrote:

What about destructors, are they always called, or is this another
optimization if the struct is in it's default .init state?


In any case, there should be 1 destructor call for every object you
declared.

Except for thing on the heap. Those are just collected, not destroyed :/


The destructor is always called. There is never a instance that gets 
destroyed without the destructor beeing called. It can happen however 
that the destructor gets called on a .init state. This happens for 
example if you use "std.algorithm.move". To be fully correct your struct 
should handle the .init state in the destructor (or assert at least so 
you can find and fix those occurences).


Kind Regards
Benjamin Thaut


Re: destructor, postblit constructor --- force calling always

2014-03-30 Thread monarch_dodra

On Monday, 31 March 2014 at 01:03:22 UTC, Carl Sturtivant wrote:
What about destructors, are they always called, or is this 
another optimization if the struct is in it's default .init 
state?


AFAIK, a destructor is always called when an object goes out of 
scope... unless, it's being moved out of scope. As a matter of 
fact, spec RVO *will* get triggered. This is what allows witting 
factory functions for types without postblit.


In any case, there should be 1 destructor call for every object 
you declared.


Except for thing on the heap. Those are just collected, not 
destroyed :/


Re: destructor, postblit constructor --- force calling always

2014-03-30 Thread Carl Sturtivant
D's structs don't have identity. That means, they can be moved 
without notice at any point in the program. AFAIK the compiler 
even does that when handling exceptions in a few cases (e.g. 
with structs on the stack). Having moveable value types allows 
for a lot of optimizations, both on the compiler side as well 
as when implementing e.g. containers in user code. So no there 
is no way to always get notified. I also proposed a move 
constructor in the past, but the idea was not well recieved. 
When I needed a move constructor, usually adding another level 
of indirection solved the problem.


Thanks for that discussion. As you might have guessed, adding 
another level of indirection is what I was trying to avoid. A 
move constructor would have taken care of my problem.


What about destructors, are they always called, or is this 
another optimization if the struct is in it's default .init state?




Re: destructor, postblit constructor --- force calling always

2014-03-30 Thread Benjamin Thaut

Am 30.03.2014 21:35, schrieb Carl Sturtivant:


Context: struct values that are (non-ref) parameters & local variables.

Is there a way to arrange that a particular struct's special
constructors (postblit,destructor) should always be called even on move,
or on destruction of a default initialized value, temporary or not,
etcetera, i.e. ensure they should always be called?

Is a struct's destructor always called when it goes out of scope, even
if it is default initialized?



D's structs don't have identity. That means, they can be moved without 
notice at any point in the program. AFAIK the compiler even does that 
when handling exceptions in a few cases (e.g. with structs on the 
stack). Having moveable value types allows for a lot of optimizations, 
both on the compiler side as well as when implementing e.g. containers 
in user code. So no there is no way to always get notified. I also 
proposed a move constructor in the past, but the idea was not well 
recieved. When I needed a move constructor, usually adding another level 
of indirection solved the problem.


Kind Regards
Benjamin Thaut


destructor, postblit constructor --- force calling always

2014-03-30 Thread Carl Sturtivant


Context: struct values that are (non-ref) parameters & local 
variables.


Is there a way to arrange that a particular struct's special 
constructors (postblit,destructor) should always be called even 
on move, or on destruction of a default initialized value, 
temporary or not, etcetera, i.e. ensure they should always be 
called?


Is a struct's destructor always called when it goes out of scope, 
even if it is default initialized?