Taking a copy of an object

kris foo at bar.com
Wed Aug 9 23:27:53 PDT 2006


Good stuff, Mikola ~ I'm with you on this. My only concern is the lack 
of a naming enforcement -- using compiler-supported operators instead 
(along with the appropriate opDup and opClone) would resolve that 
particular issue; but that's a reasonably minor detail, which could 
actually be implemented later on ...

Nice one!

- Kris


Mikola Lysenko wrote:
> Adding a virtual deep copy method to Object is a bad idea. Classes such as
> sockets or files must never be subjected to such a procedure, since the
> result would be unpredictable.
> 
> A different solution is to require each type to explicitly state if it 
> supports deep-copies at compile time.  This can be done by creating the
> convention that all classes which support deep copy define a 'clone'
> method.  For the moment, clone needs to be implemented by hand, but given
> better introspection it should be possible to create a mixin which
> automatically performs this task.
> 
> If everyone adheres to this convention, the following templates to allow
> anyone to test if a type is clonable at compile time - and easily perform
> a clone.  For completeness, there is also a shallow copy or 'dup'
> operation using the same technique.
> 
> Code:
> 
> import std.stdio, std.string;
> 
> 
> /**
>  * A clone is a deep copy of an object.  It is totally independent
>  * of the original object.  Once cloned, the original may be
>  * deleted or modified without affecting the copy.
>  *
>  * All primitives types and structs are clonable by default - except
>  * pointers.  Objects are clonable if and only if they define a 
>  * clone method.
>  *
>  * isCloneable tests if a type can be cloned.
>  *
>  * clone makes a deep copy of the original object.
>  */
> template isCloneable(T)
> {
>     static if (is(T A : A[]))
>         enum { isCloneable = isCloneable!(A) }
>     else static if (is(typeof(&T.clone)))
>         enum { isCloneable = true }
>     else static if (is(T : Object))
>         enum { isCloneable = false }
>     else static if (is(T : T *))
>         enum { isCloneable = false }
>     else
>         enum { isCloneable = true }
> }
> 
> 
> /**
>  * Creates a deep copy of the given object.
>  *
>  * Params:
>  *  t = The object we are cloning.
>  *
>  * Returns:
>  *  A deep copy of the original object.
>  */
> T clone(T)(T obj)
> {
>     static assert (isCloneable!(T));
> 
>     static if(is(T A : A[]))
>     {
>         A[] res = new A[obj.length];    
>         foreach(int i, inout A t; obj)
>             res[i] = clone!(A)(t);
>         return res;
>     }
>     else static if(is(T : Object))
>         return obj.clone;
>     else
>         return obj;
> }
> 
> 
> /**
>  * A dup operation returns a shallow copy of the given object.
>  * All referenced objects are the same as the original, however
>  * the returned object is not.
>  *
>  * All primitive types, structs and arrays are dupable.  Objects
>  * are dupable if they implement a dup method.
>  *
>  * isDupable tests if a type can be duplicated.
>  *
>  * dup creates a shallow copy of a dupable type.
>  */
> template isDupable(T)
> {
>     static if (is(typeof(&T.dup)))
>         enum { isDupable = true }
>     else static if (is(T : Object))
>         enum { isDupable = false }
>     else
>         enum { isDupable = true }
> }
> 
> 
> 
> /**
>  * Creates a shallow copy of the given object.
>  * 
>  * Params:
>  *  t = The object we are copying.
>  *
>  * Returns:
>  *  A shallow copy of the object.
>  */
> T dup(T)(T obj)
> {
>     static assert(isDupable!(T));
>     
>     static if(is(typeof(&T.dup)))
>         return obj.dup;
>     else
>         return obj;
> }
> 
> 
> 
> 
> //Test cases
> class DupMe
> {
>     this(int x)
>     {
>         this.x = x;
>     }
>     
>     DupMe dup()
>     {
>         return new DupMe(x);
>     }
>     
>     int x;
>     
>     char[] toString()
>     {
>         return format("%s", x);
>     }
> }
> 
> class CloneMe
> {
>     this(int x)
>     {
>         this.x = x;
>     }
>     
>     CloneMe clone()
>     {
>         return new CloneMe(x);
>     }
>     
>     int x;
>     
>     char[] toString()
>     {
>         return format("%s", x);
>     }
> }
> 
> class DontCloneMe
> {
>     this(int x)
>     {
>         this.x = x;
>     }
>     
>     int x;
> }
> 
> struct Test1
> {
>     int a, b, c;
> }
> 
> void main()
> {
>     writefln("isCloneable!(int) = ", isCloneable!(int));
>     writefln("isCloneable!(DontCloneMe) = ", isCloneable!(DontCloneMe));
>     writefln("isCloneable!(CloneMe) = ", isCloneable!(CloneMe));
>     writefln("isCloneable!(int*) = ", isCloneable!(int*));
>     writefln("isCloneable!(int[]) = ", isCloneable!(int[]));
>     writefln("isCloneable!(DontCloneMe[]) = ", isCloneable!(DontCloneMe[]));
>     writefln("isCloneable!(CloneMe[]) = ", isCloneable!(CloneMe[]));
>     writefln("isCloneable!(Test1) = ", isCloneable!(Test1));
>     writefln("isCloneable!(DontCloneMe[][]) = ", isCloneable!(DontCloneMe[][]));
>     writefln("isCloneable!(CloneMe[][]) = ", isCloneable!(CloneMe[][]));
>     writefln("isCloneable!(int[5]) = ", isCloneable!(int[5]));
>     
>     CloneMe c1 = new CloneMe(19);
>     CloneMe c2 = clone!(CloneMe)(c1);
>     
>     writefln("c1.x = %s, c2.x = %s", c1.toString, c2.toString);
>     
>     c2.x = 10000;
>     writefln("c1.x = %s, c2.x = %s", c1.toString, c2.toString);
>     
>     
>     CloneMe[] arr;
>     arr ~= new CloneMe(1);
>     arr ~= new CloneMe(2);
>     arr ~= new CloneMe(3);
>     arr ~= new CloneMe(4);
>     arr ~= new CloneMe(5);
>     arr ~= new CloneMe(6);
>     
>     CloneMe[] arr_clone = clone(arr);
>     
>     writefln("arr = %s\narr_clone = %s", arr, arr_clone);
>     
>     arr_clone[3].x = 10000;
>     writefln("arr = %s\narr_clone = %s", arr, arr_clone);
>     
>     
>     writefln("isDupable!(int) = ", isDupable!(int));
>     writefln("isDupable!(DontCloneMe) = ", isDupable!(DontCloneMe));
>     writefln("isDupable!(CloneMe) = ", isDupable!(CloneMe));
>     writefln("isDupable!(DupMe) = ", isDupable!(DupMe));
>     writefln("isDupable!(int*) = ", isDupable!(int*));
>     writefln("isDupable!(int[]) = ", isDupable!(int[]));
>     writefln("isDupable!(DontCloneMe[]) = ", isDupable!(DontCloneMe[]));
>     writefln("isDupable!(CloneMe[]) = ", isDupable!(CloneMe[]));
>     writefln("isDupable!(Test1) = ", isDupable!(Test1));
>     writefln("isDupable!(DontCloneMe[][]) = ", isDupable!(DontCloneMe[][]));
>     writefln("isDupable!(CloneMe[][]) = ", isDupable!(CloneMe[][]));
>     writefln("isDupable!(int[5]) = ", isDupable!(int[5]));
>     
>     
>     DupMe d1 = new DupMe(10);
>     DupMe d2 = dup(d1);
>     
>     writefln("d1.x = %s, d2.x = %s", d1.toString, d2.toString);
>     
>     d2.x = 1000;
>     writefln("d1.x = %s, d2.x = %s", d1.toString, d2.toString);
>     
>     DupMe[] darr;
>     darr ~= new DupMe(1);
>     darr ~= new DupMe(2);
>     darr ~= new DupMe(3);
>     DupMe[] darr_2 = dup(darr);
>     
>     writefln("darr = %s, darr2 = %s", darr, darr_2);
>     
>     darr_2[0].x = 100;
>     darr_2 ~= new DupMe(20);
>     
>     writefln("darr = %s, darr2 = %s", darr, darr_2);
> }
> 
> 



More information about the Digitalmars-d mailing list