Re: Can I call the default opAssign after overloading opAssign?

2012-11-25 Thread Jonathan M Davis
On Sunday, November 25, 2012 12:05:36 monarch_dodra wrote:
> AFAIK, there is no "official spec". And even if there was, the
> "de-facto" spec *is* TDPL... minus everything that could have
> changed since it's printing.

The online docs are the official spec. They're just not necessarily complete or 
up-to-date, making it so that there is no definitive answer when the compiler, 
the spec, and/or TDPL conflict. _Usually_ TDPL wins out on such arguments, but 
not always.

Regardless, the online docs clearly need to be improved before you can 
implement a compiler for the language based purely on the spec.

- Jonathan M Davis


Re: Can I call the default opAssign after overloading opAssign?

2012-11-25 Thread Dan

On Sunday, 25 November 2012 at 11:05:37 UTC, monarch_dodra wrote:


AFAIK, there is no "official spec". And even if there was, the 
"de-facto" spec *is* TDPL... minus everything that could have 
changed since it's printing.


I think TDPL is great, but there is a doc called "D Language 
Specification" which is the perfect level for reference - it is 
just now outdated and incomplete. Couldn't Walter just open 
source this document and retain final say on the updates? With 
C++ we had the ARM and Stroustrup's book. For D we have the spec 
and TDPL. Put the source of the language spec out in some markup 
language, let others help to bring it up to snuff, generate pdf's 
and other formats. The only tricky part is documenting areas 
where the language designer(s) has open issues without a 
commitment to a solution.


Take the opAssign issue in question. In TDPL 7.1.5.1 we have 
"Recall that Widget holds a private int[] member that was 
supposed to be distinct for each Widget object. Assigning w2 to 
w1 field by field assigns w2.array to w1.array—a simple 
assignment of array bounds, without actually copying the array 
contents. This needs fixing because what we want is to create a 
duplicate of the array in the source Widget and assign that 
duplicate to the target Widget."


He then goes on to describe how user code can intercept opAssign 
to make things right. But, unless I'm missing something, that is 
unnecessary because the default opAssign carries the call to 
postblit (see previous example). Now look at the issue from the 
language spec (Structs & Unions - Assignment Overload):


Struct assignment t=s is defined to be semantically equivalent to:
t = S.opAssign(s);
where opAssign is a member function of S:
S* opAssign(S s)
{   ... bitcopy *this into tmp ...
... bitcopy s into *this ...
... call destructor on tmp ...
return this;
}

This description is almost perfect, it just fails to mention 
"...bitcopy and postblit". Both docs are incorrect according to 
the behavior of default opAssign. I think the language spec 
should be fixed first and kept accurate.


Thanks
Dan



Re: Can I call the default opAssign after overloading opAssign?

2012-11-25 Thread monarch_dodra

On Sunday, 25 November 2012 at 00:12:04 UTC, Rob T wrote:


Thanks for pointing out where the postblit stuff is documented. 
When I first started learning the language, I did read that 
part a few times over, but I found it frustratingly hard to 
grasp. I will re-read that section again.


This should be MUCH more documented. Most users (in particular 
C++ users) are surprised by this behavior, and creates a great 
deal of confusion.


TDPL is a good book, but it is not the official spec, nor is it 
even a spec, it's a book that covers some aspects of how to use 
the language. How copy and assignments work in D really needs 
to be 100% documented in the language spec to ensure that it is 
officially a part of the language and not a clever compiler 
optimization that may or may not be implemented.


AFAIK, there is no "official spec". And even if there was, the 
"de-facto" spec *is* TDPL... minus everything that could have 
changed since it's printing.





Re: Can I call the default opAssign after overloading opAssign?

2012-11-24 Thread Rob T
On Saturday, 24 November 2012 at 20:47:17 UTC, Era Scarecrow 
wrote:
This kind of behavior *really* needs to be documented in 
precise detail, it's rather critical to know.


 It IS documented. TDPL - pg. 248
[quote]


Thanks for pointing out where the postblit stuff is documented. 
When I first started learning the language, I did read that part 
a few times over, but I found it frustratingly hard to grasp. I 
will re-read that section again.


TDPL is a good book, but it is not the official spec, nor is it 
even a spec, it's a book that covers some aspects of how to use 
the language. How copy and assignments work in D really needs to 
be 100% documented in the language spec to ensure that it is 
officially a part of the language and not a clever compiler 
optimization that may or may not be implemented.


To make things much clearer, the documentation should perhaps 
contain a flow chart showing the order of execution, with plenty 
of examples that show edge cases and best practices.


--rt



Re: Can I call the default opAssign after overloading opAssign?

2012-11-24 Thread Dan
On Saturday, 24 November 2012 at 20:47:17 UTC, Era Scarecrow 
wrote:

On Friday, 23 November 2012 at 22:31:46 UTC, Rob T wrote:
That's VERY interesting indeed and originally I had no idea it 
would do this without a custom opAssign at each level.


This kind of behavior *really* needs to be documented in 
precise detail, it's rather critical to know.


 It IS documented. TDPL - pg. 248
[quote]
  The second step (the part with 'transitive field') of the 
postblit copy process deserves a special mention. The rationale 
for that behavior is [i]encapsulation[/i]-the postblit 
constructor of a struct object must be called even when the 
struct is embedded in another struct object. Consider, for 
example, that we make Widget a member of another struct, which 
in turn is a member of yet another struct:


(included from pg. 246)
[code]
struct Widget {
  private int[] array;
  this(uint length) {
array = new int[length];
  }
  // postblit constructor
  this(this){
array = array.dup;
  }
  //As Before
  int get(size_t offset) { return array[offset]; }
  void set(size_t offset, int value) { array[offset] = value; }
}

struct Widget2 {
  Widget w1;
  int x;
}

struct Widget3 {
  Widget2 w2;
  string name;
  this(this) {
name = name ~ " (copy)";
  }
}
[/code]

  Now, if you want to copy around objects that contain Widgets, 
it would be pretty bad if the compiler forgot to properly copy 
the Widget subobjects. That's why when copying objects of type 
Widget2, a call to this(this) is issued for the w subobject, 
even though Widget2 does not intercept copying at all. Also, 
when copying objects of type Widget3, again this(this) is 
invoked for the field w1 of field w2. To Clarify:


[code]
unittest {
  Widget2 a;
  a.w1 = Widget(10);  //Allocate some memory
  auto b = a; // this(this) called for 
b.w

  assert(a.w1.array ~is b.w1.array);  // Pass

  Widget3 c;
  c.w2.w1 = Widget(20);
  auto d = c;   // this(this) for 
d.w2.w1

  assert(c.w.2.w.1.array !is d.w2.w1.array);  //pass
}
[/code]
[/quote]


Good catch on this(this) - it is documented well. But I think the 
questionable part is on assignment, not copy construction via 
postblit. For assignment the postblit *is* being called and the 
language spec (not TDPL) glosses over that. Also, not mentioned 
in TDPL is what happens if you do implement your own opAssign. I 
think some of the magic goes away if I'm not mistaken (i.e. those 
well-crafted postblits will not be called). I think this should 
be documented as well.


Thanks
Dan


Re: Can I call the default opAssign after overloading opAssign?

2012-11-24 Thread Era Scarecrow

On Friday, 23 November 2012 at 22:31:46 UTC, Rob T wrote:
That's VERY interesting indeed and originally I had no idea it 
would do this without a custom opAssign at each level.


This kind of behavior *really* needs to be documented in 
precise detail, it's rather critical to know.


 It IS documented. TDPL - pg. 248
[quote]
  The second step (the part with 'transitive field') of the 
postblit copy process deserves a special mention. The rationale 
for that behavior is [i]encapsulation[/i]-the postblit 
constructor of a struct object must be called even when the 
struct is embedded in another struct object. Consider, for 
example, that we make Widget a member of another struct, which in 
turn is a member of yet another struct:


(included from pg. 246)
[code]
struct Widget {
  private int[] array;
  this(uint length) {
array = new int[length];
  }
  // postblit constructor
  this(this){
array = array.dup;
  }
  //As Before
  int get(size_t offset) { return array[offset]; }
  void set(size_t offset, int value) { array[offset] = value; }
}

struct Widget2 {
  Widget w1;
  int x;
}

struct Widget3 {
  Widget2 w2;
  string name;
  this(this) {
name = name ~ " (copy)";
  }
}
[/code]

  Now, if you want to copy around objects that contain Widgets, 
it would be pretty bad if the compiler forgot to properly copy 
the Widget subobjects. That's why when copying objects of type 
Widget2, a call to this(this) is issued for the w subobject, even 
though Widget2 does not intercept copying at all. Also, when 
copying objects of type Widget3, again this(this) is invoked for 
the field w1 of field w2. To Clarify:


[code]
unittest {
  Widget2 a;
  a.w1 = Widget(10);  //Allocate some memory
  auto b = a; // this(this) called for b.w
  assert(a.w1.array ~is b.w1.array);  // Pass

  Widget3 c;
  c.w2.w1 = Widget(20);
  auto d = c;   // this(this) for d.w2.w1
  assert(c.w.2.w.1.array !is d.w2.w1.array);  //pass
}
[/code]
[/quote]


Re: Can I call the default opAssign after overloading opAssign?

2012-11-23 Thread Rob T

On Monday, 19 November 2012 at 12:10:32 UTC, Dan wrote:

[...]
provide it - you do not need an opAssign at all, as your 
postblit will be called. I think this is a step up over C++.


The example below prints:
--
Begin assign
postblit A
End assign
--

import std.stdio;
import std.traits;

struct A {
  this(this) { c = c.dup; writeln("postblit A"); }
  char[] c;

}
struct B { A a; }
struct C { B b; }
struct D { C c; }

void main() {
  D d1, d2;
  d1.c.b.a.c = ['a','b','c'];
  writeln("Begin assign");
  d2 = d1;
  writeln("End assign");
}


That's VERY interesting indeed and originally I had no idea it 
would do this without a custom opAssign at each level.


This kind of behavior *really* needs to be documented in precise 
detail, it's rather critical to know.


--rt



Re: Can I call the default opAssign after overloading opAssign?

2012-11-23 Thread Dan

On Monday, 19 November 2012 at 12:10:32 UTC, Dan wrote:

Just following up to get confirmation. Hopefully Johnathan or 
similar expert can follow up.


Here is a strong statement:

If for any struct S you implement a postblit then there is no 
need to implement opAssign to get a working assignment operator 
from a type S because by design postblit is already called by 
default opAssign. This is the behavior I see, but I may be 
missing something since the language specification does not 
mention postblit, only blit.


Thanks
Dan

On Monday, 19 November 2012 at 05:22:38 UTC, Jonathan M Davis 
wrote:

On Monday, November 19, 2012 06:01:55 Rob T wrote:


postblit constructors and opAssign aren't really related. The 
postblit
constructor is used when a _new_ instance is being constructed 
(it plays the
same role as a copy constructor in C++). opAssign overloads 
the assignment
operator and is only used when the assignment operator is 
used, which does
_not_ happen when contstructing a new instance but only when 
replacing the

value of an instance with that of another.


Is this correct? From a implementation point of view it looks 
like opAssign is related to postblit in that it does call 
postblit first.

From the spec:
Struct assignment t=s is defined to be semantically 
equivalent to:

  t = S.opAssign(s);
where opAssign is a member function of S:
S* opAssign(S s)
{   ... bitcopy *this into tmp ...
  ... bitcopy s into *this ...
  ... call destructor on tmp ...
  return this;
}

It does not say postblit as well, but it does call it.
When assigning one object into another it will first blit, then 
custom postblit if you have written one. A benefit of this is, 
if you want deep copy semantics and postblit does the work to 
provide it - you do not need an opAssign at all, as your 
postblit will be called. I think this is a step up over C++.


The example below prints:
--
Begin assign
postblit A
End assign
--

import std.stdio;
import std.traits;

struct A {
  this(this) { c = c.dup; writeln("postblit A"); }
  char[] c;

}
struct B { A a; }
struct C { B b; }
struct D { C c; }

void main() {
  D d1, d2;
  d1.c.b.a.c = ['a','b','c'];
  writeln("Begin assign");
  d2 = d1;
  writeln("End assign");
}





Re: Can I call the default opAssign after overloading opAssign?

2012-11-19 Thread Rob T
On Monday, 19 November 2012 at 09:37:35 UTC, Jonathan M Davis 
wrote:

On Monday, November 19, 2012 10:29:21 Rob T wrote:

the D language specification (which is currently MIA).


The online documentation _is_ the official spec, though it 
definitely doesn't
have enough detail to be unambiguous, and in some cases, it's 
not properly up-

to-date.

- Jonathan M Davis


Well yes, there is a spec, and it's pretty good in some areas, 
but also just not precise enough in other areas, such with what 
we're discussing in here. I'd like to see the mechanism 
surrounding the copy/move semantics described in full details as 
a part of the language spec, otherwise it's a bit risky to rely 
on these behaviors if they are only considered as compiler 
optimizations.


I know that Walter started D from the POV of a compiler 
developer, so he probably does consider the optimizations to be a 
part of the spec, but I'd like to see that in writing somewhere 
to make it rock-solid official. There's mention of this in the 
TDPL but again it's written as being an optimization, although 
you could also get the impression is is a language feature, but 
it's not exactly clear.


Note that I'm picking on this topic because it's a foundation 
just about everything else it built up on, so it really needs to 
be thoroughly documented as part of the spec.


--rt


Re: Can I call the default opAssign after overloading opAssign?

2012-11-19 Thread Andrej Mitrovic
On 11/19/12, Rob T  wrote:
> perhaps best
> done using the C libs memcopy function.

I think the safest thing you can do is:

void oldAssign(Type rhs)
{
this.tupleof = rhs.tupleof;
}


Re: Can I call the default opAssign after overloading opAssign?

2012-11-19 Thread Dan
On Monday, 19 November 2012 at 05:22:38 UTC, Jonathan M Davis 
wrote:

On Monday, November 19, 2012 06:01:55 Rob T wrote:


postblit constructors and opAssign aren't really related. The 
postblit
constructor is used when a _new_ instance is being constructed 
(it plays the
same role as a copy constructor in C++). opAssign overloads the 
assignment
operator and is only used when the assignment operator is used, 
which does
_not_ happen when contstructing a new instance but only when 
replacing the

value of an instance with that of another.


Is this correct? From a implementation point of view it looks 
like opAssign is related to postblit in that it does call 
postblit first.

From the spec:
Struct assignment t=s is defined to be semantically 
equivalent to:

  t = S.opAssign(s);
where opAssign is a member function of S:
S* opAssign(S s)
{   ... bitcopy *this into tmp ...
  ... bitcopy s into *this ...
  ... call destructor on tmp ...
  return this;
}

It does not say postblit as well, but it does call it.
When assigning one object into another it will first blit, then 
custom postblit if you have written one. A benefit of this is, if 
you want deep copy semantics and postblit does the work to 
provide it - you do not need an opAssign at all, as your postblit 
will be called. I think this is a step up over C++.


The example below prints:
--
Begin assign
postblit A
End assign
--

import std.stdio;
import std.traits;

struct A {
  this(this) { c = c.dup; writeln("postblit A"); }
  char[] c;

}
struct B { A a; }
struct C { B b; }
struct D { C c; }

void main() {
  D d1, d2;
  d1.c.b.a.c = ['a','b','c'];
  writeln("Begin assign");
  d2 = d1;
  writeln("End assign");
}



Re: Can I call the default opAssign after overloading opAssign?

2012-11-19 Thread Jonathan M Davis
On Monday, November 19, 2012 10:29:21 Rob T wrote:
> the D language specification (which is currently MIA).

The online documentation _is_ the official spec, though it definitely doesn't 
have enough detail to be unambiguous, and in some cases, it's not properly up-
to-date.

- Jonathan M Davis


Re: Can I call the default opAssign after overloading opAssign?

2012-11-19 Thread Rob T
On Monday, 19 November 2012 at 06:32:56 UTC, Jonathan M Davis 
wrote:
I'm not sure. Close certainly. But if any member variables 
define an opAssign,
then the compiler probably calls them rather than doing a 
simple memcpy. I'm
not sure though. If it does, then a memcpy would not exhibit 
the same
behavior, and the only way to get the same behavior would be to 
copy each
member variable one by one. If it doesn't, then a memcpy would 
do the same

thing as the default behavior.


I think you are right. There's was a post a couple days ago on an 
issue concerning a nested struct with opAssign. The parent had no 
opAssign, but the nested struct did, and for some reason the 
nested opAssign was not being called in one case, but was being 
called in another. Something about being relocatable?


I am a bit worried though as to why you'd even want to skip 
opAssign like
that. At the moment, I can't think of any legitimate use cases 
for doing that

(though that obviously doesn't mean that you don't have one).

- Jonathan M Davis


No reason I can see at this time either. I just want to fully 
understand what D is doing because it's not clearly documented. 
What worries me most, is if I end up relying on behaviors that 
end up being implemented as clever compiler optimizations rather 
than being a part of the D language specification (which is 
currently MIA).


--rt



Re: Can I call the default opAssign after overloading opAssign?

2012-11-18 Thread Jonathan M Davis
On Monday, November 19, 2012 07:21:10 Rob T wrote:
> I think you've cleared things up for me.
> 
> When I define an opAssign, I'm not really overriding a default
> opAssign, because there is none, instead I'm overriding the
> default behavior which is to perform a memcopy-like operation.
> 
> So if I defined an opAssign function, but for some odd reason I
> wanted to execute the default assignment behavior, then I can
> still do it by performing a memcopy-like operation, perhaps best
> done using the C libs memcopy function.
> 
> Correct?

I'm not sure. Close certainly. But if any member variables define an opAssign, 
then the compiler probably calls them rather than doing a simple memcpy. I'm 
not sure though. If it does, then a memcpy would not exhibit the same 
behavior, and the only way to get the same behavior would be to copy each 
member variable one by one. If it doesn't, then a memcpy would do the same 
thing as the default behavior.

I am a bit worried though as to why you'd even want to skip opAssign like 
that. At the moment, I can't think of any legitimate use cases for doing that 
(though that obviously doesn't mean that you don't have one).

- Jonathan M Davis


Re: Can I call the default opAssign after overloading opAssign?

2012-11-18 Thread Rob T

I think you've cleared things up for me.

When I define an opAssign, I'm not really overriding a default 
opAssign, because there is none, instead I'm overriding the 
default behavior which is to perform a memcopy-like operation.


So if I defined an opAssign function, but for some odd reason I 
wanted to execute the default assignment behavior, then I can 
still do it by performing a memcopy-like operation, perhaps best 
done using the C libs memcopy function.


Correct?

--rt


Re: Can I call the default opAssign after overloading opAssign?

2012-11-18 Thread Jonathan M Davis
On Monday, November 19, 2012 06:01:55 Rob T wrote:
> I assume that when I define an opAssign, only the opAssign that I
> define gets called, which means there's no blit or postblit being
> called ever again.
> 
> I may be thoroughly confused at this point. Is there both a blit
> and a postblit, and an optional opAssign that when specified will
> override both?

postblit constructors and opAssign aren't really related. The postblit 
constructor is used when a _new_ instance is being constructed (it plays the 
same role as a copy constructor in C++). opAssign overloads the assignment 
operator and is only used when the assignment operator is used, which does 
_not_ happen when contstructing a new instance but only when replacing the 
value of an instance with that of another.

S s1;
S s2 = s1; // postblit
s1 = s2; // opAssign
foo(s1); // postblit

If you don't define a postblit constructor, then when a new instance is created 
from another, then the original is memcpyed/blitted to the new one. If you 
_do_ define a postblit constructor, then the original is memcpyed/blitted and 
then _after_ that the postblit constructor is called so that you have the 
opportunity to deep copy the pieces that need to be deep copied.

If you don't define opAssign, then when assigning from the one instance to 
another, a memcpy/blit is done to copy the data over. If you _do_ define a 
opAssign, then no memcpy/blit is made at all, but rather opAssign is called.

- Jonathan M Davis


Re: Can I call the default opAssign after overloading opAssign?

2012-11-18 Thread Rob T
I assume that when I define an opAssign, only the opAssign that I 
define gets called, which means there's no blit or postblit being 
called ever again.


I may be thoroughly confused at this point. Is there both a blit 
and a postblit, and an optional opAssign that when specified will 
override both?


--rt


Re: Can I call the default opAssign after overloading opAssign?

2012-11-17 Thread Jonathan M Davis
On Saturday, November 17, 2012 15:33:48 Kagamin wrote:
> AFAIK, opAssign and postblit are different operators. Postblit is
> called after blit, and opAssign is called instead of blit.

postlbit is making a new copy of an object, whereas opAssign is replacing the 
state of a pre-existing object. They're fundamentally different.

- Jonathan M Davis


Re: Can I call the default opAssign after overloading opAssign?

2012-11-17 Thread Kagamin
AFAIK, opAssign and postblit are different operators. Postblit is 
called after blit, and opAssign is called instead of blit.


Re: Can I call the default opAssign after overloading opAssign?

2012-11-16 Thread Jonathan M Davis
On Friday, November 16, 2012 21:31:26 Rob T wrote:
> My understanding is that a struct will have a default postblit
> opAssign. What I want to know is if I can call the default
> opAssign after overriding it, or is it inaccessible?
> 
> I know that I do not have to execute the default after
> overriding, but if I can call it, I'd like to know because in
> some cases it may be useful to run the default. So this is just a
> general knowledge kind of question at this point.

I don't think that it even exists. Basically, if you don't define an opAssign, 
one is provided for you. If you do define one, then you already have one, so 
the compiler doesn't provide one.

- Jonathan M Davis