www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Implementing .dup / clone for class hierarchies

reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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 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.
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
prev sibling parent reply guslay <guslay gmail.com> writes:
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
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
guslay wrote:
 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.
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. --bb
Dec 16 2007
next sibling parent reply guslay <guslay gmail.com> writes:
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.
 
 --bb
I think .NET as something similar with MemberwiseClone() [protected] http://www.go-mono.com/docs/index.aspx?link=M%3ASystem.Object.MemberwiseClone()
Dec 16 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
guslay wrote:
 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.  
Oops. I meant "it *would* be fine". 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.

 --bb
I think .NET as something similar with MemberwiseClone() [protected] http://www.go-mono.com/docs/index.aspx?link=M%3ASystem.Object.MemberwiseClone()
--bb
Dec 16 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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