May 04, 2012
On Friday, 4 May 2012 at 20:35:57 UTC, Era Scarecrow wrote:

>  Well the result seems to be true enough.. It also seems to work if both are const. Why didn't I consider that before? Maybe it should be noted in the next TDPL book.

 As I look at my code I realize why now. Pointers and arrays... that's why...

test.d(16): Error: cannot implicitly convert expression (x2.y) of type const(int[]) to int[]

 Which is why I was trying to suggest the ref was const since I wasn't modifying and could ensure it (just copying between buffers) but the temporary I'm taking over.. So that doesn't work now.

 This means I'll likely need 2 copies of the ref; 1 const and 1 non-const.

import std.stdio;
import std.conv;

struct X {
  int x;
  int[] y;

  this (int x1, int y1) {
    x = x1; y ~= y1;
  }

  //const here won't work.
  ref X opAssign(const X x2) {
    writeln("from temporary?");
    x=x2.x;
    y=x2.y; //breaks here
    return this;
  }

  ref X opAssign(ref const X x2) {
    writeln("copy specific values?");
    x=x2.x;
	y[]=x2.y[];
    return this;
  }
}

X fn() {
  return X(1,2);
}

void main()
{
  X x = X(3,4);
  X y = X(5,6);

  x = y;
  writeln("Should selectively copy (ref)- ", to!string(x));

  x = fn();
  writeln("should overwrite all - ", to!string(x));
}
May 04, 2012
On Fri, 04 May 2012 16:47:07 -0400, Era Scarecrow <rtcvb32@yahoo.com> wrote:

> On Friday, 4 May 2012 at 20:35:57 UTC, Era Scarecrow wrote:
>
>>  Well the result seems to be true enough.. It also seems to work if both are const. Why didn't I consider that before? Maybe it should be noted in the next TDPL book.
>
>   As I look at my code I realize why now. Pointers and arrays... that's why...
>
> test.d(16): Error: cannot implicitly convert expression (x2.y) of type const(int[]) to int[]
>
>   Which is why I was trying to suggest the ref was const since I wasn't modifying and could ensure it (just copying between buffers) but the temporary I'm taking over.. So that doesn't work now.
>
>   This means I'll likely need 2 copies of the ref; 1 const and 1 non-const.
>
> import std.stdio;
> import std.conv;
>
> struct X {
>    int x;
>    int[] y;
>
>    this (int x1, int y1) {
>      x = x1; y ~= y1;
>    }
>
>    //const here won't work.
>    ref X opAssign(const X x2) {
>      writeln("from temporary?");
>      x=x2.x;
>      y=x2.y; //breaks here

y[] = x2.y[];
// as you did below

>      return this;
>    }
>
>    ref X opAssign(ref const X x2) {
>      writeln("copy specific values?");
>      x=x2.x;
> 	y[]=x2.y[];
>      return this;
>    }
> }
>
> X fn() {
>    return X(1,2);
> }
>
> void main()
> {
>    X x = X(3,4);
>    X y = X(5,6);
>
>    x = y;
>    writeln("Should selectively copy (ref)- ", to!string(x));
>
>    x = fn();
>    writeln("should overwrite all - ", to!string(x));
> }

btw, are you sure the lengths will always be identical?  Otherwise, you may want to do y.length = x2.y.length first.

-Steve
May 04, 2012
On Friday, 4 May 2012 at 21:12:55 UTC, Steven Schveighoffer wrote:
> y[] = x2.y[];
> // as you did below

 That may deal with the language requirements, but the ideal was to take over the temporary; If not the ref would copy the contents. I wonder if I will need to have 2 copies of each opAssign to satisfy everything.

> btw, are you sure the lengths will always be identical?  Otherwise, you may want to do y.length = x2.y.length first.

 This example code doesn't cover that, but yes I have checks in place for such an occurrence. Likely 99% of the time copying will be between identical sizes.
May 04, 2012
On Fri, 04 May 2012 17:26:02 -0400, Era Scarecrow <rtcvb32@yahoo.com> wrote:

> On Friday, 4 May 2012 at 21:12:55 UTC, Steven Schveighoffer wrote:
>> y[] = x2.y[];
>> // as you did below
>
>   That may deal with the language requirements, but the ideal was to take over the temporary; If not the ref would copy the contents. I wonder if I will need to have 2 copies of each opAssign to satisfy everything.

OK, I see what you are trying to do.  I'll have to think about it some more.  I'm very concerned that you could potentially end up calling this function with an lvalue, which would make this a disasterous strategy.

-Steve
May 04, 2012
On Friday, 4 May 2012 at 21:29:14 UTC, Steven Schveighoffer wrote:
> On Fri, 04 May 2012 17:26:02 -0400, Era Scarecrow
>> On Friday, 4 May 2012 at 21:12:55 UTC, Steven Schveighoffer wrote:
>>> y[] = x2.y[];
>>> // as you did below
>>
>> That may deal with the language requirements, but the ideal  was to take over the temporary; If not the ref would copy the  contents. I wonder if I will need to have 2 copies of each  opAssign to satisfy everything.

> OK, I see what you are trying to do.  I'll have to think about  it some more.  I'm very concerned that you could potentially  end up calling this function with an lvalue, which would make  this a disastrous strategy.

 Well let's see if we can recap it into perspective so we're on the same Page. I'm trying to make a struct that on assignment will copy it's contents.

 On opAssign In both cases the mutable and const/immutable rvalue will copy it's contents. If it's a temporary (which is always returned as mutable) I want to take it over rather than copy the contents. The contents are likely referenced elsewhere already.

 Here's a stripped version of my struct, obviously with lots of missing unneeded stuff.

struct X {
  ubyte[] buffer;

  //from temporary being returned.
  ref X opAssign(X rhs) {
    this.buffer = rhs.buffer;
    return this;
  }

  //copying from right side to left.
  ref X opAssign(ref X rhs) {
    if (buffer.length != rhs.length) {
      buffer.length = rhs.buffer.length;
      ptr = buffer.ptr;
    }

    buffer[] = rhs.buffer[];
    return this;
  }
}
May 05, 2012
On Saturday, May 05, 2012 01:18:13 Era Scarecrow wrote:
> On Friday, 4 May 2012 at 21:29:14 UTC, Steven Schveighoffer wrote:
> > On Fri, 04 May 2012 17:26:02 -0400, Era Scarecrow
> > 
> >> On Friday, 4 May 2012 at 21:12:55 UTC, Steven Schveighoffer
> >> 
> >> wrote:
> >>> y[] = x2.y[];
> >>> // as you did below
> >> 
> >> That may deal with the language requirements, but the ideal was to take over the temporary; If not the ref would copy the contents. I wonder if I will need to have 2 copies of each opAssign to satisfy everything.
> > 
> > OK, I see what you are trying to do.  I'll have to think about it some more.  I'm very concerned that you could potentially end up calling this function with an lvalue, which would make this a disastrous strategy.
> 
>   Well let's see if we can recap it into perspective so we're on
> the same Page. I'm trying to make a struct that on assignment
> will copy it's contents.
> 
>   On opAssign In both cases the mutable and const/immutable rvalue
> will copy it's contents. If it's a temporary (which is always
> returned as mutable) I want to take it over rather than copy the
> contents. The contents are likely referenced elsewhere already.

As I understand it, you don't need to do _anything_ special to avoid having a temporary copied when passed to your function. Because it's a temporary, it should be moved into the function parameter, not copied. No postblit or opAssign will be executed. It's only when it's _not_ a temporary that a copy will be made. And even in _that_ case, it should be a move if that function call is the last place that the variable is used.

So, I believe that you're trying to avoid copies that will never happen anyway. They would if you were dealing with C++ - particularly C++98 - but not in D (C++11 fixes the problem via move constructors).

- Jonathan M Davis
May 05, 2012
On Saturday, 5 May 2012 at 02:21:06 UTC, Jonathan M Davis wrote:
> As I understand it, you don't need to do _anything_ special to  avoid having a temporary copied when passed to your function. Because it's a  temporary, it should be moved into the function parameter, not copied. No  postblit or opAssign will be executed. It's only when it's _not_ a  temporary that a copy will be made. And even in _that_ case, it should be a move if  that function call is the last place that the variable is used.

 That's what I originally thought too... But when I'm dealing with the temporary there's something broken. So if I only have only the ref.

Error: function X.opAssign (ref X rhs) is not callable using argument types (X)

so;
struct X {
  ref X opAssign(ref X rhs);
}

 X func();

struct Y {
  X x;
  this() {
    x = func();
  }
}

 Unless there's a default it drops back to, it is only seeing the one assign function.

> So, I believe that you're trying to avoid copies that will  never happen anyway. They would if you were dealing with C++ - particularly  C++98 - but not in D (C++11 fixes the problem via move constructors).

 I'm not trying to avoid copying (Blocks are small so it's likely a tiny cost); I'm trying to keep arrays separate.  Let's add to my example; I Hope I got it right based on the behavior I'm seeing.

struct X {
  ubyte[] buffer;
  this(ubyte[] b) {
    buffer = b;
  }
  ref X opAssign(ref X rhs);
}

X func() {
  return X(new ubyte[5]);
}

void func2(){
 ubyte[15] buff;
 X x1 = X(buff[0 .. 5]);
 X x2 = X(buff[5 .. 10]);
 const X x3 = X(buff[10 .. $]);

 x1 = x2; //should use ref, opAssign required to keep addresses separate
 assert(x1.buff !is x2.buff); //must pass, else value semantics I'm using breaks

 x1 = func(); //default copy can work, but fails due to opAssign(ref X) existing. Works with opAssign(X)

 x1 = x3;  //should fail and needs a (const X) or (ref const X).

 //but if you change to (ref const X),
 x1 = x2;  //then opAssign(X) runs, making arrays x1 & x2 point to the same place; not what i want.
}

 In my thinking i make (ref const X) then mutable versions should work too; but somehow it's deciding to use (X) instead of (ref const X). I'm promising I won't change the data, not requiring the data can't be changed.
May 05, 2012
On Saturday, May 05, 2012 05:15:00 Era Scarecrow wrote:
> On Saturday, 5 May 2012 at 02:21:06 UTC, Jonathan M Davis wrote:
> > As I understand it, you don't need to do _anything_ special to avoid having a temporary copied when passed to your function. Because it's a  temporary, it should be moved into the function parameter, not copied. No  postblit or opAssign will be executed. It's only when it's _not_ a  temporary that a copy will be made. And even in _that_ case, it should be a move if that function call is the last place that the variable is used.
> 
>   That's what I originally thought too... But when I'm dealing
> with the temporary there's something broken. So if I only have
> only the ref.
> 
> Error: function X.opAssign (ref X rhs) is not callable using
> argument types (X)
> 
> so;
> struct X {
>    ref X opAssign(ref X rhs);
> }
> 
>   X func();
> 
> struct Y {
>    X x;
>    this() {
>      x = func();
>    }
> }
> 
>   Unless there's a default it drops back to, it is only seeing the
> one assign function.

If you've declared an opAssign, I'd be very surprised if _any_ assignment worked which didn't work with the opAssign that you declared. Once you've declared an opAssign, you've taken over the assignment operator, and you need to define it for all of the types that you want it to work with.

> > So, I believe that you're trying to avoid copies that will never happen anyway. They would if you were dealing with C++ - particularly  C++98 - but not in D (C++11 fixes the problem via move constructors).
> 
>   I'm not trying to avoid copying (Blocks are small so it's likely
> a tiny cost); I'm trying to keep arrays separate.  Let's add to
> my example; I Hope I got it right based on the behavior I'm
> seeing.
> 
> struct X {
>    ubyte[] buffer;
>    this(ubyte[] b) {
>      buffer = b;
>    }
>    ref X opAssign(ref X rhs);
> }
> 
> X func() {
>    return X(new ubyte[5]);
> }
> 
> void func2(){
>   ubyte[15] buff;
>   X x1 = X(buff[0 .. 5]);
>   X x2 = X(buff[5 .. 10]);
>   const X x3 = X(buff[10 .. $]);
> 
>   x1 = x2; //should use ref, opAssign required to keep addresses
> separate
>   assert(x1.buff !is x2.buff); //must pass, else value semantics
> I'm using breaks
> 
>   x1 = func(); //default copy can work, but fails due to
> opAssign(ref X) existing. Works with opAssign(X)
> 
>   x1 = x3;  //should fail and needs a (const X) or (ref const X).
> 
>   //but if you change to (ref const X),
>   x1 = x2;  //then opAssign(X) runs, making arrays x1 & x2 point
> to the same place; not what i want.
> }
> 
>   In my thinking i make (ref const X) then mutable versions should
> work too; but somehow it's deciding to use (X) instead of (ref
> const X). I'm promising I won't change the data, not requiring
> the data can't be changed.

According to http://dlang.org/function.html:

---------
Func­tions are over­loaded based on how well the ar­gu­ments to a func­tion can match up with the pa­ra­me­ters. The func­tion with the best match is se­ lected. The lev­els of match­ing are:

1. no match
2. match with im­plicit con­ver­sions
3. match with con­ver­sion to const
4. exact match
---------

It picks opAssign(X) over opAssign(ref const X) with an argument of type X, because opAssign(X) is an exact match (#4) whereas opAssign(ref const X) requires a conversion to const (#3).

- Jonathan M Davis
May 05, 2012
On Saturday, 5 May 2012 at 03:32:06 UTC, Jonathan M Davis wrote:
> If you've declared an opAssign, I'd be very surprised if _any_  assignment worked which didn't work with the opAssign that you declared.  Once you've declared an opAssign, you've taken over the assignment  operator, and you need to define it for all of the types that you want it to work with.

 So define all four... gotcha...

> According to http://dlang.org/function.html:
>
> ---------
> Func­tions are over­loaded based on how well the  ar­gu­ments to a func­tion can match up with the pa­ra­me­ters. The func­tion with the  best match is se­lected. The lev­els of match­ing are:
>
> 1. no match
> 2. match with im­plicit con­ver­sions
> 3. match with con­ver­sion to const
> 4. exact match
> ---------
>
> It picks opAssign(X) over opAssign(ref const X) with an argument of type X,because opAssign(X) is an exact match (#4) whereas opAssign(ref const X)requires a conversion to const (#3).

 I guess there's one question left. Where does the struct live? If it closes at ending of the scope I believed that meant the stack, but then this would be illegal:

ref X func() {
  return X(new ubyte[5]); //reference to local variable!
}

 If it lives on the heap too rather than the stack, that'd good to know. I don't recall it specifying in TDPL, but it would be easy enough to assume it does (Coming from C).

May 05, 2012
> On Saturday, 5 May 2012 at 03:32:06 UTC, Jonathan M Davis wrote:
>> ---------
>> Func­tions are over­loaded based on how well the   ar­gu­ments to a func­tion can match up with the  pa­ra­me­ters. The func­tion with the  best match is  se­lected. The lev­els of match­ing are:
>>
>> 1. no match
>> 2. match with im­plicit con­ver­sions
>> 3. match with con­ver­sion to const
>> 4. exact match
>> ---------
>>

Hmm maybe it should have a preference for Lvalue vs Rvalue... So... Walter or Andrei?

 1. no match
 2. match with im­plicit con­ver­sions (Lvalue required)
 3. match with con­ver­sion to const (Lvalue required)
 4. match with im­plicit con­ver­sions
 5. match with con­ver­sion to const
 6. exact match