Taking a copy of an object

Mikola Lysenko mclysenk at mtu.edu
Wed Aug 9 21:30:15 PDT 2006


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