digitalmars.D.learn - Implementing .dup / clone for class hierarchies
- Bill Baxter (57/57) Dec 09 2007 I like the .dup idea in D, but how do you implement it for a class
- Steven Schveighoffer (10/10) Dec 10 2007 If you have dup return an Object, then you can use the classinfo to allo...
- Bill Baxter (101/110) Dec 10 2007 No you'll get a complete copy because dup and copy are both virtual
- guslay (6/9) Dec 16 2007 I came across this thread the other day, that suggest doing bitwise copy...
- Bill Baxter (34/53) Dec 16 2007 Good sleuth work!
- guslay (3/33) Dec 16 2007 I think .NET as something similar with MemberwiseClone() [protected]
- Bill Baxter (4/42) Dec 16 2007 Oops. I meant "it *would* be fine".
- Bill Baxter (9/14) Dec 16 2007 I realized this isn't quite true. .dup on arrays is depth=1, rather
I like the .dup idea in D, but how do you implement it for a class hierarchy? Are there existing libraries that deal with this problem well? In a nutshell the problem is this: class Base { Base dup() { auto ret = new Base(); // copy members of this to ret return ret; } } class DerivedA : Base { DerivedA dup() { auto ret = new DerivedA(); // copy members of base to ret // copy members of this to ret return ret; } } class DerivedB : Base { DerivedB dup() { auto ret = new DerivedB(); // copy members of base to ret // copy members of this to ret return ret; } } The main issue is the "copy members of base to ret" part. The way I've been doing it is to add a virtual copy method to every level of the hierarchy: /** copy other on top of this */ void copy(Class other) { this.x = other.x; // etc... } Then derived classes can call the super's copy in their copy: void copy(DerivedA other) { super.copy(other); // copy my derived fields } and dup at every level can call copy: DerivedA dup() { auto ret = new DerivedA(); ret.copy(this); return ret; } So is this a good way to do it? It seems pretty simple and straightforward, but I'm kind of nervous about the whole area of copying because I know there's volumes written about it in C++. I'm sure I don't recall all the ins and outs of everything I've read about doing copying in C++. But maybe there are just fewer gotchas in D. Also I'm curious to know if there are any emerging trends on what to call the copy member function because life is better if we don't all invent our own convention. --bb
Dec 09 2007
If you have dup return an Object, then you can use the classinfo to allocate a new object of the most derived type (I think), and then just override the copy method. The only issue is casting once you get the result, I like how arrays don't need to be casted on dup. But this could be overcome with a wrapper dup that calls the base method and just casts the return. Ugly, but doable. One issue with your implementation, if I have a DerivedA reference to a DerivedB class, and I call dup, I'm going to get a DerivedA class only, wouldn't I want a complete copy (i.e. a DerivedB instance)? -Steve
Dec 10 2007
Let me reply in reverse order: Steven Schveighoffer wrote:One issue with your implementation, if I have a DerivedA reference to a DerivedB class, and I call dup, I'm going to get a DerivedA class only, wouldn't I want a complete copy (i.e. a DerivedB instance)?No you'll get a complete copy because dup and copy are both virtual methods and so the most derived versions will be called. Only the reference you get back will not be typed as the most derived type. I believe that's all possible thanks to "covariant return types". (Also in the example both DerivedA and DerivedB were derived directly from Base, so you wouldn't get a DerivedB from a DerivedA no matter how hard you try. ;-) ) Here's a complete compiling example: ---------------------------------- class Base { int x = 0; Base dup() { auto ret = new Base(); // copy members of this to ret ret.copy(this); return ret; } void copy(Base other) { writefln("copy Base"); x = other.x; } string toString() { return "Base"; } } class DerivedA : Base { int y = 0; DerivedA dup() { auto ret = new DerivedA(); ret.copy(this); return ret; } void copy(DerivedA other) { writefln("copy A"); super.copy(other); y = other.y; } string toString() { return "DerivedA"; } } void main() { auto a = new DerivedA; Base a_as_base = a; Base a_dup = a_as_base.dup; writefln("a_dup says I'm a: %s", a_dup); // prints // copyA // copy Base // a_dup says I'm a: DerivedA } As for your suggestion:If you have dup return an Object, then you can use the classinfo toallocatea new object of the most derived type (I think), and then justoverride thecopy method. The only issue is casting once you get the result, Ilike howarrays don't need to be casted on dup. But this could be overcomewith awrapper dup that calls the base method and just casts the return.Ugly, butdoable.I tried it but to get it to work you have to make copy() take a Base rather than a Derived, otherwise DerivedA's copy() wont get called from Base.dup. Thus copy methods will also all need casts with this method. Here's the code: ------------------------------------------- import std.stdio; class Base { int x = 0; Base dup() { auto ret = cast(Base)this.classinfo.create; // copy members of this to ret ret.copy(this); return ret; } void copy(Base other) { writefln("CopyBase"); x = other.x; } string toString() { return "Base"; } } class DerivedA : Base { int y = 0; DerivedA dup() { return cast(DerivedA)super.dup(); } void copy(Base other) { writefln("CopyA"); super.copy(other); auto d = cast(DerivedA)other; y = d.y; } string toString() { return "DerivedA"; } } void main() { auto a = new DerivedA; Base a_as_base = a; Base a_dup = a_as_base.dup; writefln("a_dup says I'm a: %s", a_dup); } --bb
Dec 10 2007
Bill Baxter Wrote:I like the .dup idea in D, but how do you implement it for a class hierarchy? Are there existing libraries that deal with this problem well?I came across this thread the other day, that suggest doing bitwise copy of the class for shallow copying. I don't know how safe that is. http://www.digitalmars.com/d/archives/digitalmars/D/learn/1625.html Another thread that caught my attention was this one. http://www.digitalmars.com/d/archives/digitalmars/D/41145.html#N41264 Those threads are a little old. I too would like to know if someone came up with a state of the art solution or an informal naming convention.
Dec 16 2007
guslay wrote:Bill Baxter Wrote:Good sleuth work! Ha, looks like I posted a message on that second thread. And created a wiki page! Totally forgot about that. Hence we see the futility of creating wiki pages. :-) Also see the recent thread over on digitalmars.D called "what was wrong with struct & class in C++" For some reason everyone seemed to like Mikola's suggestion of 'clone' for deep copy and 'dup' for shallow copy, but the odd thing about that is that .dup on built-in arrays is a deep copy. So it seems it should mean a deep copy for classes too. Bad idea to go out of your way to mimic the syntax of D built-ins but give it a different meaning, IMHO. The first thread is more about automatic shallow copying and Burton Radons came up with this compiler-specific hack: """ private extern (C) Object _d_newclass (ClassInfo info); Object shallow_copy (Object value) { if (value is null) return null; void *copy = _d_newclass (value.classinfo); size_t size = value.classinfo.init.length; copy [8 .. size] = (cast (void *) value) [8 .. size]; return cast (Object) copy; } Better to start at that offset to avoid copying the synchronisation handle over, plus it's pointless work. """ That makes me shudder. But it wouldn't be fine if it were implemented as part of object.d. I think that functionality would actually be very useful to have as member of ClassInfo. Then a base class .dup function could shallow copy everything first so that derived .dups that only add value members wouldn't need to do anything. --bbI like the .dup idea in D, but how do you implement it for a class hierarchy? Are there existing libraries that deal with this problem well?I came across this thread the other day, that suggest doing bitwise copy of the class for shallow copying. I don't know how safe that is. http://www.digitalmars.com/d/archives/digitalmars/D/learn/1625.html Another thread that caught my attention was this one. http://www.digitalmars.com/d/archives/digitalmars/D/41145.html#N41264 Those threads are a little old. I too would like to know if someone came up with a state of the art solution or an informal naming convention.
Dec 16 2007
Bill Baxter Wrote:.. The first thread is more about automatic shallow copying and Burton Radons came up with this compiler-specific hack: """ private extern (C) Object _d_newclass (ClassInfo info); Object shallow_copy (Object value) { if (value is null) return null; void *copy = _d_newclass (value.classinfo); size_t size = value.classinfo.init.length; copy [8 .. size] = (cast (void *) value) [8 .. size]; return cast (Object) copy; } Better to start at that offset to avoid copying the synchronisation handle over, plus it's pointless work. """ That makes me shudder. But it wouldn't be fine if it were implemented as part of object.d. I think that functionality would actually be very useful to have as member of ClassInfo. Then a base class .dup function could shallow copy everything first so that derived .dups that only add value members wouldn't need to do anything. --bbI think .NET as something similar with MemberwiseClone() [protected] http://www.go-mono.com/docs/index.aspx?link=M%3ASystem.Object.MemberwiseClone()
Dec 16 2007
guslay wrote:Bill Baxter Wrote:Oops. I meant "it *would* be fine". I think that functionality would actually be very.. The first thread is more about automatic shallow copying and Burton Radons came up with this compiler-specific hack: """ private extern (C) Object _d_newclass (ClassInfo info); Object shallow_copy (Object value) { if (value is null) return null; void *copy = _d_newclass (value.classinfo); size_t size = value.classinfo.init.length; copy [8 .. size] = (cast (void *) value) [8 .. size]; return cast (Object) copy; } Better to start at that offset to avoid copying the synchronisation handle over, plus it's pointless work. """ That makes me shudder. But it wouldn't be fine if it were implemented as part of object.d.--bbuseful to have as member of ClassInfo. Then a base class .dup function could shallow copy everything first so that derived .dups that only add value members wouldn't need to do anything. --bbI think .NET as something similar with MemberwiseClone() [protected] http://www.go-mono.com/docs/index.aspx?link=M%3ASystem.Object.MemberwiseClone()
Dec 16 2007
Bill Baxter wrote:guslay wrote:Bill Baxter Wrote:For some reason everyone seemed to like Mikola's suggestion of 'clone' for deep copy and 'dup' for shallow copy, but the odd thing about that is that .dup on built-in arrays is a deep copy.I realized this isn't quite true. .dup on arrays is depth=1, rather than truly "deep" (depth=N) or shallow (depth=0). So maybe Mikola was right. We can take 'dup' to mean "copy one more level than you'd get from the rudimentary assignment: a = b". So for built-in arrays dup means copy the stuff pointed to by ptr, but for a class, stuff pointed to by a ptr member wouldn't be copied. --bb
Dec 16 2007