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