View mode: basic / threaded / horizontal-split · Log in · Help
May 04, 2012
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
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
Re: opAssign and const?
> 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
1 2 3
Top | Discussion index | About this forum | D home