digitalmars.D - Copying structs with pointers
- Peter Alexander (38/38) Jul 20 2011 Just came across this issue today. I wanted to create a typical value
- Jonathan M Davis (4/54) Jul 20 2011 Postblit doesn't work with const or immutable right now:
- Peter Alexander (20/22) Jul 20 2011 Is that the issue though? I believe it's a deeper issue.
- Jonathan M Davis (23/50) Jul 20 2011 It means that regardless of whether there's a deeper issue, using const ...
- Peter Alexander (2/6) Jul 20 2011 My thoughts exactly.
- Andrei Alexandrescu (4/13) Jul 20 2011 Walter and I are on it and know what's to be done there. But we couldn't...
Just came across this issue today. I wanted to create a typical value type wrapper around a heap allocated object. I even created a nice postblit constructor like a good D programmer. struct Ptr { private int* p; this(int x) { p = new int; *p = x; } this(this) { int x = *p; p = new int; *p = x; } ... } void main() { const Ptr a = Ptr(1); Ptr b = a; // error } Problem is, this code is illegal because you can't create a non-const copy of a const value type that contains pointers or references (due to transitive const). I can get around it in this particular instance by creating a dup property (like a C++ copy constructor): struct Ptr { property const Ptr dup() { return Ptr(*p); } } But it still means that my struct is non-copyable in general. For example, I can't fill a range with a const Ptr. const Ptr a = Ptr(1); Ptr[10] arr; std.algorithm.fill(arr[], a); // error I understand why the shallow copy isn't allowed in general (it would break transitive const), but as the dup property demonstrates, I am able to create a deep copy of Ptr without violating const-correctness -- the problem is that I am not able to express this using a postblit constructor. Am I missing something? How are you supposed to define value semantics on structs containing pointers?
Jul 20 2011
On 2011-07-20 14:49, Peter Alexander wrote:Just came across this issue today. I wanted to create a typical value type wrapper around a heap allocated object. I even created a nice postblit constructor like a good D programmer. struct Ptr { private int* p; this(int x) { p = new int; *p = x; } this(this) { int x = *p; p = new int; *p = x; } ... } void main() { const Ptr a = Ptr(1); Ptr b = a; // error } Problem is, this code is illegal because you can't create a non-const copy of a const value type that contains pointers or references (due to transitive const). I can get around it in this particular instance by creating a dup property (like a C++ copy constructor): struct Ptr { property const Ptr dup() { return Ptr(*p); } } But it still means that my struct is non-copyable in general. For example, I can't fill a range with a const Ptr. const Ptr a = Ptr(1); Ptr[10] arr; std.algorithm.fill(arr[], a); // error I understand why the shallow copy isn't allowed in general (it would break transitive const), but as the dup property demonstrates, I am able to create a deep copy of Ptr without violating const-correctness -- the problem is that I am not able to express this using a postblit constructor. Am I missing something? How are you supposed to define value semantics on structs containing pointers?Postblit doesn't work with const or immutable right now: http://d.puremagic.com/issues/show_bug.cgi?id=4867 - Jonathan M Davis
Jul 20 2011
On 20/07/11 11:06 PM, Jonathan M Davis wrote:Postblit doesn't work with const or immutable right now: http://d.puremagic.com/issues/show_bug.cgi?id=4867Is that the issue though? I believe it's a deeper issue. An example: struct Ptr { int* p; this(int x) { p = new int; *p = x; } this(this) { /+ do nothing +/ } // (!!!) const int get() { return *p; } void set(int x) { *p = x; } } void main() { const Ptr a = Ptr(1); Ptr b = a; // shallow copy! const removed! b.set(2); assert(a.get() == 1); // FAIL } If you allow this then it means that you can get a non-const pointer through a const object, which breaks the transitivity of const.
Jul 20 2011
On 2011-07-20 15:30, Peter Alexander wrote:On 20/07/11 11:06 PM, Jonathan M Davis wrote:It means that regardless of whether there's a deeper issue, using const with postblit isn't going to work.Postblit doesn't work with const or immutable right now: http://d.puremagic.com/issues/show_bug.cgi?id=4867Is that the issue though? I believe it's a deeper issue.An example: struct Ptr { int* p; this(int x) { p = new int; *p = x; } this(this) { /+ do nothing +/ } // (!!!) const int get() { return *p; } void set(int x) { *p = x; } } void main() { const Ptr a = Ptr(1); Ptr b = a; // shallow copy! const removed! b.set(2); assert(a.get() == 1); // FAIL } If you allow this then it means that you can get a non-const pointer through a const object, which breaks the transitivity of const.Hmmm. This is definitely a problem. It should be possible to use postblits when the object being copied is const and has pointers and/or references. If not, that's fairly crippling. But without doing some sort of flow analysis, I don't see how the problem that you present can be fixed. If it were a copy constructor, it would be straightforward - everything gets default initialized and then you assign stuff from the const original, making copies where required. But a postblit does a shallow copy _first_, which means that either this is forced to point to const (thereby making it so that you can't do anything but a shallow copy and that the copy must be const), or the type system gets twisted a bit by allowing this to point to mutable even though it's const and the compiler then must ensure that copies are made (or at least that the original values are not kept) or const would be bypassed (and even that probably wouldn't work, since then you'd have to worry about the member variables being passed to functions as mutable inside of the constructor when they're really const underneath). And I don't think that either of those solutions really works. Maybe we actually need a copy constructor of some kind for this sort of case. I don't see how to get around it with a postblit. This is definitely a big problem. - Jonathan M Davis
Jul 20 2011
On 20/07/11 11:50 PM, Jonathan M Davis wrote:Maybe we actually need a copy constructor of some kind for this sort of case. I don't see how to get around it with a postblit. This is definitely a big problem. - Jonathan M DavisMy thoughts exactly.
Jul 20 2011
On 7/20/11 6:12 PM, Peter Alexander wrote:On 20/07/11 11:50 PM, Jonathan M Davis wrote:Walter and I are on it and know what's to be done there. But we couldn't find the time to do it yet. AndreiMaybe we actually need a copy constructor of some kind for this sort of case. I don't see how to get around it with a postblit. This is definitely a big problem. - Jonathan M DavisMy thoughts exactly.
Jul 20 2011