On Thursday, 15 November 2012 at 17:24:44 UTC, Jonathan M Davis wrote:
On Thursday, November 15, 2012 14:21:41 Dan wrote:
I do not see how copy constructors can help. Receiving const(T)
or const(T) ref to anything and hoping to copy it is the
challenge and doing it from a copy constructor is no easier than
a postblit.

Doing it from a postblit is impossible.

I understand and this makes sense. postblit is not a deep copy, it is only what you choose to make it.

Doing it from a copy constructor _is_
possible (assuming that they're added to the language - they don't help you
any until they are).

I question the need for this - at least for structs composed of structs, dynamic arrays and associative arrays. Classes are another issue.

The core problem with postblit constructor is that it
does a shallow copy _before_ entering the postblit constructor. This seems great at first, because then you only have to copy stuff in the postblit itself which actually needs a deep copy. But it fails completely with const and immutable, because it's illegal to alter anything which is const or immutable - even by casting them to mutable temporarily to make the change (doing so is undefined behavior). So, you can't change _anything_ in a const postblit constructor, meaning that you can't make a deep copy of anything.


Understood. I don't think the problem with postblit is what it does _before_ I do my thing. The problem for this case is it can not guarantee what I do *in* my postblit and therefore it does not and can not know I'm leaving with no sharing. I don't see this as a failure, though. You want a copy, then you need to make a copy. Find a way other than postblit. The suggestion is use copy constructors instead. I don't see how this will help - but I have not found the proposal (do you have a link?). I have read DIP10 which was an early attempt to get at this stuff - but I think it did not go far.

A copy constructor, on the other hand, does not do any copying beforehand. It all must still be constructed. The copy constructor then constructs the new object with deep copies of the data. This works with const and immutable, because it's construction rather than mutation. The postblit constructor fails because it requires you to use mutation, which doesn't work with const.


A copy ctor does not copy anything beforehand. True. Then the idea with this new feature is to leave it to you to copy the fields from inside your new copy ctor. But you will no more be able to copy them from a copy ctor than you were from a postblit because you have a const instance as your source and something extra is needed for each field. Now if that something extra is more copy constructors all down the line - I think that would work but is unnecessary. I think that the real need is a global dup or deep copy function. It should be possible without touching our structs and I think I have something fairly close when it comes to structs, associative arrays, and dynamic arrays.

So, as nice an idea as it is, the postblit constructor is a failure.

I feel it is not a failure. It is doing its job just fine. The natural tendency, especially coming from c++ is to assume const values assigned into non-const lvalues should be ok - because we are used to deep copy semantics just working. The only problem is copy construction (and therefore postblit) is not the right tool to copy const instances with reference semantics. I think a global dup function would be the right tool and it is doable, hopefully with a reasonable performance overhead.

The issue is you want to copy a const(T). Suppose I give you a function that works like this (by the way this is working code (I use 2.061))

import std.stdio;
import opmix.mix;
struct S {
  char[] c;
}

void foo(ref const(S) s) {
  // I really need a copy here
  auto copy = s.gdup;
  assert(s.c.ptr != copy.c.ptr);
  writeln(copy);
}
void main() {
  S s = {['a','b','c']};
  foo(s);
}

Now if you had this gdup, all your copy ctors would look like this:
struct S {
  char[] c;
  S(ref const(S) s) {
     swap(this, s.gdup);
  }
}

But if you had that gdup in the first place, there would be no need for copy ctors. Besides, if the compiler could *know* that my new copy ctor was really doing a deep copy - why make me write it in the first place. Just provide the implementation for me. Allowing me to do anything in the copy ctor allows me to introduce sharing which is forbidden.

Does this make sense?

Thanks
Dan


Reply via email to