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