www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Postblit constructor

reply Jiyan <jiyan jiyan.info> writes:
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
Feb 28 2018
next sibling parent reply Jiyan <jiyan jiyan.info> writes:
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?
Feb 28 2018
parent Eduard Staniloiu <edi33416 gmail.com> writes:
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.
Mar 02 2018
prev sibling parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
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
Mar 02 2018