August 08, 2006
kris wrote:
> 
> BTW: one concern I'd have with a root-Object dup() method is the need to cast the returned instance. Presumeably a more specific compiler implementation (of object.dup) would sidestep that need for casting?

There is no need for casting. Since D supports covariant return types, the overriding methods can return their specific types:

  class Foo {
    Foo Clone() {
      return new Foo();
    }
  }

  class FooBar : Foo {
    override FooBar Clone() {
      return new FooBar();
    }
  }

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
August 08, 2006
Derek wrote:
> 
>> Also, it is redundant to specify a shallow copy function for each class: the code is the same for any class. It is something like:
>>    Object ShallowCopy(Object obj) {
>>      int len = obj.classinfo.init.length;
>>      auto data = (cast(ubyte*) obj)[0..len];
>>      return cast(Object) data.dup.ptr;
>>    }
>> So there should not be two different functions for each kind of copy, there should be only one, which conceptually is defined to do a deep copy.
> 
> Not sure I agree here. The shallow copy coulod be generic as you
> demonstrated, but a deep copy is very Object-specific and would need to be
> defined in the class that needed it. 
> 
> 

I quite agree with what you said, I misstated my comments: when I said "there should not be two different functions", by 'functions' I meant "functions defined by the user". That is, there should not be two different functions defined by the user, as the shallow copy one is redundant, and should exist already.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
August 08, 2006
Derek wrote:
> On Thu, 03 Aug 2006 14:12:03 -0600, Hasan Aljudy wrote:
> 
> 
> Please, feel free to suggest other things that might be suitable for a
> deep-copy operator. One that would be usable on all data types and meants
> that the information in the left-hand-side thingy would be copied and the
> right-hand thingy would 'hold' that copy.
> 
> For objects, it would invoke onDup/onCopy/onClone/onDeepCopy/onWhatever
> (the name doesn't matter) if it existed, otherwise its a compile time
> error. The function would return the same datatype as the object that owns
> the function, meaning that Foo.onDup can't return a Bar or its parent type
> or an interface - it must return a Foo object instance.
> 
> For arrays it is identical to the .dup property.
> 
> For basic datatypes it is identical to moving the bit value from one to
> another variable (no conversion or transformations allowed).
> 
> Therefore the lefthand side and righthand side thingy must be the same
> datatype.
> 

I've just realized a little nuance that both I and the rest of us are tripping on: There are two similar but distinct notions of a copy operation, not just one.
The first one, usually called duping or cloning, is creating a new instance which is a copy of an existing item/data-instance (a one operand operation).
The second one, is copying one item to another existing one (a two operand operation), which is more general than the former.
Note the difference between the terms "create a copy" and simply "copy".

We've been mixing these two concepts a bit, but they should be clearly separate. It won't work well to try to have one do the job of the other, rather we can have both in the language:

  foo.opCopy(bar)  // copies bar into foo;
  foo := bar       // same as above
  foo.Clone()      // creates a (deep) copy of foo

note that Clone is the same (conceptually at least) as:
  (new typeof(foo)).opCopy(foo);
or even:
  foo.dup.opCopy(foo);
if .dup does a shallow copy.

One could also think of a two-operand shallow-copy operation, but that seems like going too far, I don't believe that would be useful.

In fact, I'm also not that sure if it is much useful for a datatype that already has a deep self-copy function to have shallow self-copy too. Derek mention the example of arrays, but even so...

Clone should be a virtual method in the Object hierarchy. As for opCopy, I'm not sure. If it is a final method, you're limited in what you can do with polymorphism. If it is virtual method, then the method has to have a parameter of type Object, and so one has to write some dynamic dispatch code in the method to handle the proper runtime types.

The names could be other of course, but my suggestion is as per the examples above: self-copy is called "Clone" or "dup", and two-operand copy is "Copy" or "opCopy". The names "dup" and "Clone" are likely inappropriate for a Copy operation.
Additionally, we could maybe also call "Clone" to the *deep* copying, and "dup" to *shallow* copying, if there is a need to have both.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
August 08, 2006
Bruno Medeiros wrote:
> Derek wrote:
> 
>> On Thu, 03 Aug 2006 14:12:03 -0600, Hasan Aljudy wrote:
>>
>>
>> Please, feel free to suggest other things that might be suitable for a
>> deep-copy operator. One that would be usable on all data types and meants
>> that the information in the left-hand-side thingy would be copied and the
>> right-hand thingy would 'hold' that copy.
>>
>> For objects, it would invoke onDup/onCopy/onClone/onDeepCopy/onWhatever
>> (the name doesn't matter) if it existed, otherwise its a compile time
>> error. The function would return the same datatype as the object that owns
>> the function, meaning that Foo.onDup can't return a Bar or its parent type
>> or an interface - it must return a Foo object instance.
>>
>> For arrays it is identical to the .dup property.
>>
>> For basic datatypes it is identical to moving the bit value from one to
>> another variable (no conversion or transformations allowed).
>>
>> Therefore the lefthand side and righthand side thingy must be the same
>> datatype.
>>
> 
> I've just realized a little nuance that both I and the rest of us are tripping on: There are two similar but distinct notions of a copy operation, not just one.


Speak for yourself, Bruno: all of this is simply taken for granted in some of the other posts.


> The first one, usually called duping or cloning, is creating a new instance which is a copy of an existing item/data-instance (a one operand operation).
> The second one, is copying one item to another existing one (a two operand operation), which is more general than the former.
> Note the difference between the terms "create a copy" and simply "copy".
> 
> We've been mixing these two concepts a bit, but they should be clearly separate. It won't work well to try to have one do the job of the other, rather we can have both in the language:
> 
>   foo.opCopy(bar)  // copies bar into foo;
>   foo := bar       // same as above
>   foo.Clone()      // creates a (deep) copy of foo
> 
> note that Clone is the same (conceptually at least) as:
>   (new typeof(foo)).opCopy(foo);
> or even:
>   foo.dup.opCopy(foo);
> if .dup does a shallow copy.
> 
> One could also think of a two-operand shallow-copy operation, but that seems like going too far, I don't believe that would be useful.
> 
> In fact, I'm also not that sure if it is much useful for a datatype that already has a deep self-copy function to have shallow self-copy too. Derek mention the example of arrays, but even so...
> 
> Clone should be a virtual method in the Object hierarchy. As for opCopy, I'm not sure. If it is a final method, you're limited in what you can do with polymorphism. If it is virtual method, then the method has to have a parameter of type Object, and so one has to write some dynamic dispatch code in the method to handle the proper runtime types.
> 
> The names could be other of course, but my suggestion is as per the examples above: self-copy is called "Clone" or "dup", and two-operand copy is "Copy" or "opCopy". The names "dup" and "Clone" are likely inappropriate for a Copy operation.
> Additionally, we could maybe also call "Clone" to the *deep* copying, and "dup" to *shallow* copying, if there is a need to have both.
> 
August 08, 2006
Bruno Medeiros wrote:
> kris wrote:
> 
>>
>> BTW: one concern I'd have with a root-Object dup() method is the need to cast the returned instance. Presumeably a more specific compiler implementation (of object.dup) would sidestep that need for casting?
> 
> 
> There is no need for casting. Since D supports covariant return types, the overriding methods can return their specific types:
> 
>   class Foo {
>     Foo Clone() {
>       return new Foo();
>     }
>   }
> 
>   class FooBar : Foo {
>     override FooBar Clone() {
>       return new FooBar();
>     }
>   }
> 


Yes, we're well aware of the covariance aspect. However, we'd been talking about /one/ method to handle all shallow class copy (perhaps in the root Object, as was noted). Why one method? So (a) you don't have to add the above to each and every class that might possibly be copied at some point in the future, and (b) you don't end up being confused when you dup a class and wind-up with a partial copy (and a cast requirement) when a superclass method is invoked instead.

Covariance is all well and good, but we stepped beyond that point early on. Perhaps you'll re-read the first paragraph again and understand it better?



August 08, 2006
kris wrote:
> Bruno Medeiros wrote:
>> kris wrote:
>>
>>>
>>> BTW: one concern I'd have with a root-Object dup() method is the need to cast the returned instance. Presumeably a more specific compiler implementation (of object.dup) would sidestep that need for casting?
>>
>>
>> There is no need for casting. Since D supports covariant return types, the overriding methods can return their specific types:
>>
>>   class Foo {
>>     Foo Clone() {
>>       return new Foo();
>>     }
>>   }
>>
>>   class FooBar : Foo {
>>     override FooBar Clone() {
>>       return new FooBar();
>>     }
>>   }
>>
> 
> 
> Yes, we're well aware of the covariance aspect. However, we'd been talking about /one/ method to handle all shallow class copy (perhaps in the root Object, as was noted). Why one method? So (a) you don't have to add the above to each and every class that might possibly be copied at some point in the future, and (b) you don't end up being confused when you dup a class and wind-up with a partial copy (and a cast requirement) when a superclass method is invoked instead.
> 
> Covariance is all well and good, but we stepped beyond that point early on. Perhaps you'll re-read the first paragraph again and understand it better?
> 
> 

Hum, you're right, I thought you were talking about deep copy.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
August 08, 2006
kris wrote:
> Bruno Medeiros wrote:
>> Derek wrote:
>>
>>> On Thu, 03 Aug 2006 14:12:03 -0600, Hasan Aljudy wrote:
>>>
>>>
>>> Please, feel free to suggest other things that might be suitable for a
>>> deep-copy operator. One that would be usable on all data types and meants
>>> that the information in the left-hand-side thingy would be copied and the
>>> right-hand thingy would 'hold' that copy.
>>>
>>> For objects, it would invoke onDup/onCopy/onClone/onDeepCopy/onWhatever
>>> (the name doesn't matter) if it existed, otherwise its a compile time
>>> error. The function would return the same datatype as the object that owns
>>> the function, meaning that Foo.onDup can't return a Bar or its parent type
>>> or an interface - it must return a Foo object instance.
>>>
>>> For arrays it is identical to the .dup property.
>>>
>>> For basic datatypes it is identical to moving the bit value from one to
>>> another variable (no conversion or transformations allowed).
>>>
>>> Therefore the lefthand side and righthand side thingy must be the same
>>> datatype.
>>>
>>
>> I've just realized a little nuance that both I and the rest of us are tripping on: There are two similar but distinct notions of a copy operation, not just one.
> 
> 
> Speak for yourself, Bruno: all of this is simply taken for granted in some of the other posts.
> 
> 

I was writing a reply in how that was not so, and people where blending the two things, but... Whoa! I just realized you are right, I was way off-course and I've been misunderstanding this thread since the beginning. To make sure, going back:

Derek Wrote:
[...]
> 
> And maybe one day (hoping against precedent) that Walter will actually see
> that an operator for copying stuff is not such a stupid idea.
> 
>    auto backup := q; // invokes q.onCopy() if it exists.

Note the last paragraph. What would "auto backup := q;" do? Would it be the same as:
  auto backup = q.onCopy()
right? Then yes, I've seriously misunderstood it, but I think I had some reasons to, I'll comment later.

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
August 10, 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);
}


August 10, 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);
> }
> 
> 
August 10, 2006
On Thu, 10 Aug 2006 00:30:15 -0400, Mikola Lysenko wrote:

> Adding a virtual deep copy method to Object is a bad idea.

...

> A different solution is to require each type to explicitly state if it supports deep-copies at compile time.
...

> 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.

Yes. This is most of what I was trying to get across.

The only problem is the phrase "If everyone adheres to this convention". That will never happen, people being as they are. This is why I'd like these capabilities to be supported by the compiler via the use of operators that invoke the associated 'op' function.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
10/08/2006 4:31:55 PM