Jump to page: 1 2
Thread overview
Array of class intances
Apr 08, 2008
YY
Apr 08, 2008
Regan Heath
Apr 08, 2008
Regan Heath
Apr 08, 2008
BCS
Apr 08, 2008
bearophile
Apr 08, 2008
BCS
Apr 09, 2008
YY
Apr 09, 2008
Simen Kjaeraas
Apr 09, 2008
Frits van Bommel
Apr 08, 2008
lutger
Apr 08, 2008
lutger
April 08, 2008
Suppose I have a class :

class C {
  int a;
  int b;
  this(int a1, int b1) {
    a = a1;
    b = b1;
  }
}

And I want to make an array of class C instances :

C[] inst;
inst ~= new C(1,2);
inst ~= new C(3,4);

What happened if I make a copy of inst using dup? Are the values or the pointers are copied?

C[] copyinst = inst.dup;

When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means it copies the contents.

But when I do this :

inst.a += 3;
assert(inst.a != copyinst.a);

It also fails.

What's the best method to copy (clone) array of instances?

April 08, 2008
YY wrote:
> Suppose I have a class :
> 
> class C {
>   int a;
>   int b;
>   this(int a1, int b1) {
>     a = a1;
>     b = b1;
>   }
> }
> 
> And I want to make an array of class C instances :
> 
> C[] inst;
> inst ~= new C(1,2);
> inst ~= new C(3,4);
> 
> What happened if I make a copy of inst using dup? Are the values or the pointers are copied?
> 
> C[] copyinst = inst.dup;
> 
> When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means it copies the contents.

This assert tells you that the data pointer of the arrays is not equal, which means each array has it's own copy of the class references.

But, it doesn't mean the class instances themselves have been duplicated.

This shouldn't fail:

assert(inst[0] == copyinst[0]);

meaning the first item in each array is the same reference, you can see the value of the reference by doing this:

writefln("%x", cast(void*)inst[0]);
writefln("%x", cast(void*)copyinst[0]);

> But when I do this :
> 
> inst.a += 3;
> assert(inst.a != copyinst.a);
> 
> It also fails.

That is because inst[0] and copyinst[0] both refer to the same class reference, therefore inst[0].a is the same variable as copyinst[0].a

> What's the best method to copy (clone) array of instances?

Add this method to your class C

  C dup()
  {
  	return new C(a,b);
  }

Then, instead of this:

C[] copyinst = inst.dup;

use:

C[] copyinst;
foreach(i; inst)
	copyinst ~= i.dup;

Regan
April 08, 2008
Regan Heath wrote:
> YY wrote:
>> Suppose I have a class :
>>
>> class C {
>>   int a;
>>   int b;
>>   this(int a1, int b1) {
>>     a = a1;
>>     b = b1;
>>   }
>> }
>>
>> And I want to make an array of class C instances :
>>
>> C[] inst;
>> inst ~= new C(1,2);
>> inst ~= new C(3,4);
>>
>> What happened if I make a copy of inst using dup? Are the values or the pointers are copied?
>>
>> C[] copyinst = inst.dup;
>>
>> When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means it copies the contents.
> 
> This assert tells you that the data pointer of the arrays is not equal, which means each array has it's own copy of the class references.
> 
> But, it doesn't mean the class instances themselves have been duplicated.
> 
> This shouldn't fail:
> 
> assert(inst[0] == copyinst[0]);

The above assert should of course read:

assert(inst[0] is copyinst[0]);

Regan
April 08, 2008
YY wrote:

> Suppose I have a class :
> 
> class C {
>   int a;
>   int b;
>   this(int a1, int b1) {
>     a = a1;
>     b = b1;
>   }
> }
> 
> And I want to make an array of class C instances :
> 
> C[] inst;
> inst ~= new C(1,2);
> inst ~= new C(3,4);
> 
> What happened if I make a copy of inst using dup? Are the values or the pointers are copied?

The values are copied. But the values in this case are Objects, which are always pointers under the hood. So in the end pointers are copied by value ;)

> C[] copyinst = inst.dup;
> 
> When I tried to assert(inst.ptr == copyinst.ptr) it fails, which means it
> copies the contents.

'inst.ptr == copyinst.ptr' compares the adressess of the arrays themselves,
independent of their content. It means the arrays are stored in different
place in memory, but the content can still be the same.
Try this:  inst[0] == copyinst[0]  // true
and this: &inst[0] == &copyinst[0] // false

> But when I do this :
> 
> inst.a += 3;
> assert(inst.a != copyinst.a);
> 
> It also fails.
> 
> What's the best method to copy (clone) array of instances?

There is no standard way, perhaps somebody has written something?

If haven't found a need for this myself yet, I prefer to use structs is these cases. You could implement an .dup or .clone method in your classes and create such a functions yourself, something like this (caution, not tested):

 T[] dup(T)(T[] a)
 {
    static if (is(typeof(T.dup)))
    {
        T[] result;
        result.length = a.length;
        foreach(i, val; a)
            result[i] = val.dup;
        return result;
    }
    else
        return a.dup;
 }





April 08, 2008
lutger wrote:

> If haven't found a need for this myself yet, I prefer to use structs is these cases. You could implement an .dup or .clone method in your classes and create such a functions yourself, something like this (caution, not tested):

Sorry for my careless spelling, I'm a bit tired.
April 08, 2008
Regan Heath wrote:

> C[] copyinst;
> foreach(i; inst)
>     copyinst ~= i.dup;
> 
> Regan

this will give somewhat better performance because the ~= will allocate and copy a lot.

C[] copyinst;
copyinst.length = inst.length;
foreach(i,c; inst) copyinst[i] = c.dup;


if you want compact code and don't mind it being a bit confusing:

C[] copyinst = inst.dup;
foreach(inout i; inst) i = i.dup;
April 08, 2008
BCS:
> C[] copyinst = inst.dup;
> foreach(inout i; inst) i = i.dup;

I suggest you to use ref instead of inout, I presume inout keyword will be removed.



lutger:
T[] dup(T)(T[] a)
{
    static if (is(typeof(T.dup)))
    {
        T[] result;
        result.length = a.length;
        foreach(i, val; a)
            result[i] = val.dup;
        return result;
    }
    else
        return a.dup;
}

Nice.

Something like this (untested, it may need debugging!) may be useful for nested arrays (IsArray is true for dynamic or static arrays and false in every other situation):

template DeconstArrType(T) {
    // similar to std.bind.DynamicArrayType
    static if (IsArray!(T))
        alias typeof(T[0])[] DeconstArrType;
    else
        alias T DeconstArrType;
}

DeconstArrType!(TySub)[] deepDup(TySub)(TySub[] seq) {
    static if (is(typeof(TySub.dup))) {
        auto result = new DeconstArrType!(TySub)[seq.length];
        foreach (i, sub; seq)
            result[i] = deepDup(sub);
        return result;
    } else {
        return seq.dup;
    }
}

A generic deepcopy() function that works with everything (that needs the support of a special method in Object class too) may be useful.

Bye,
bearophile
April 08, 2008
bearophile wrote:
> 
> Something like this may be useful for nested arrays

At one point I wrote a template that bundled up an array into a ubyte buffer in a way that could be shipped across a wire and rebuilt it on the other end. I didn't care how deep the arrays were nested.
April 09, 2008
> Something like this (untested, it may need debugging!) may be useful for nested arrays (IsArray is true for dynamic or static arrays and false in every other situation):
> 
> template DeconstArrType(T) {
>     // similar to std.bind.DynamicArrayType
>     static if (IsArray!(T))
>         alias typeof(T[0])[] DeconstArrType;
>     else
>         alias T DeconstArrType;
> }
> 
> DeconstArrType!(TySub)[] deepDup(TySub)(TySub[] seq) {
>     static if (is(typeof(TySub.dup))) {
>         auto result = new DeconstArrType!(TySub)[seq.length];
>         foreach (i, sub; seq)
>             result[i] = deepDup(sub);
>         return result;
>     } else {
>         return seq.dup;
>     }
> }
> 
> A generic deepcopy() function that works with everything (that needs the support of a special method in Object class too) may be useful.
> 

Great, thanks for the hints. I have another question. Do I have to delete each and every member of the array? Like this :

foreach (ref i; inst) delete i;
inst.length = 0;

If I only do this :

inst.length = 0;

will all the instance items be automatically captured by garbage collector?

April 09, 2008
On Wed, 09 Apr 2008 20:52:05 +0200, YY <yyudhistira@hotmail.com> wrote:

>
>> Something like this (untested, it may need debugging!) may be useful for nested arrays (IsArray is true for dynamic or static arrays and false in every other situation):
>>
>> template DeconstArrType(T) {
>>     // similar to std.bind.DynamicArrayType
>>     static if (IsArray!(T))
>>         alias typeof(T[0])[] DeconstArrType;
>>     else
>>         alias T DeconstArrType;
>> }
>>
>> DeconstArrType!(TySub)[] deepDup(TySub)(TySub[] seq) {
>>     static if (is(typeof(TySub.dup))) {
>>         auto result = new DeconstArrType!(TySub)[seq.length];
>>         foreach (i, sub; seq)
>>             result[i] = deepDup(sub);
>>         return result;
>>     } else {
>>         return seq.dup;
>>     }
>> }
>>
>> A generic deepcopy() function that works with everything (that needs the support of a special method in Object class too) may be useful.
>>
>
> Great, thanks for the hints. I have another question. Do I have to delete each and every member of the array? Like this :
>
> foreach (ref i; inst) delete i;
> inst.length = 0;
>
> If I only do this :
>
> inst.length = 0;
>
> will all the instance items be automatically captured by garbage collector?

I believe the easiest would be to do inst = null;
« First   ‹ Prev
1 2