Thread overview
Struct copy, how?
Jan 03, 2009
nobody
Jan 03, 2009
BCS
Jan 03, 2009
nobody
Jan 03, 2009
bearophile
Jan 04, 2009
nobody
Jan 04, 2009
Daniel Keep
Jan 04, 2009
Christopher Wright
Jan 05, 2009
Daniel Keep
January 03, 2009
(D 1.0)

After some problems with my program I found where my problems arose.

In my mini example: A function that is supposed to create a new bear from 2
exisiting bears ends up altering the original bears.
I suspect it has to do with the line ' Bear newBear = bear1;', in that it
doesn't copy the contents of the struct.
Is there a way to copy a struct without resorting to iterating over all its
elements manually?

module main;

import std.stdio;


struct Claw
{
 int n;
}

struct Bear
{
 Claw[] claw;
}

struct StoredBears
{
 Bear[] bear;
}
StoredBears storedBears;


Bear mixBears(Bear bear1, Bear bear2)
{
 Bear newBear = bear1;

 writefln(storedBears.bear[0].claw[0].n);
 writefln(storedBears.bear[1].claw[0].n);
 writefln(bear1.claw[0].n);
 writefln(bear2.claw[0].n);
 writefln(newBear.claw[0].n);
 writefln();

 newBear.claw[0].n = bear2.claw[0].n;

 writefln(storedBears.bear[0].claw[0].n);
 writefln(storedBears.bear[1].claw[0].n);
 writefln(bear1.claw[0].n);
 writefln(bear2.claw[0].n);
 writefln(newBear.claw[0].n);

 return newBear;
}


void main()
{
 //create 2 bears and put them in the storedBears struct created above
 Bear storedBear1;
 storedBear1.claw.length = 1;
 storedBear1.claw[0].n = 1;
 storedBears.bear ~= storedBear1;

 Bear storedBear2;
 storedBear2.claw.length = 1;
 storedBear2.claw[0].n = 2;
 storedBears.bear ~= storedBear2;

 //create a new bear from the 2 bears
 Bear newBear = mixBears(storedBears.bear[0],storedBears.bear[1]);
}


January 03, 2009
Reply to nobody,

> (D 1.0)
> 
> After some problems with my program I found where my problems arose.
> 
> In my mini example: A function that is supposed to create a new bear
> from 2
> exisiting bears ends up altering the original bears.
> I suspect it has to do with the line ' Bear newBear = bear1;', in that
> it
> doesn't copy the contents of the struct.
> Is there a way to copy a struct without resorting to iterating over
> all its
> elements manually?

I think you almost spotted the cause. Bear contains an array (a reference type) of Claws. When you copy it you get a new struct with a copy of that array (again a reference). Because that array is a reference type it still referees to that same set of Claws. You can fix this by copying/duping the array

Bear newBear = bear1;
newBear.claw = bear1.claw.dup;

IIRC struct now have an opAssign that can burry this in the struct code rather than the client code. 

However this still has a few problems: 1, if claw contains a reference type you will now have 2 Claws that refer to the same thing and 2, you need to maintain opAssign to be sure that it copies all members.

One way to attack both of these would be to use a template to build a generic deep copy that uses compile time refection to copy all the members, duping arrays of PODS, copying basic types and recursively deep copying reference types and arrays of them.


January 03, 2009
"BCS" <ao@pathlink.com> wrote in message news:78ccfa2d380248cb3b2f9739eb46@news.digitalmars.com...
> Reply to nobody,
>
>> (D 1.0)
>>
>> After some problems with my program I found where my problems arose.
>>
>> In my mini example: A function that is supposed to create a new bear
>> from 2
>> exisiting bears ends up altering the original bears.
>> I suspect it has to do with the line ' Bear newBear = bear1;', in that
>> it
>> doesn't copy the contents of the struct.
>> Is there a way to copy a struct without resorting to iterating over
>> all its
>> elements manually?
>
> I think you almost spotted the cause. Bear contains an array (a reference type) of Claws. When you copy it you get a new struct with a copy of that array (again a reference). Because that array is a reference type it still referees to that same set of Claws. You can fix this by copying/duping the array

Oh I see. I find it a bit odd that it's copied in this manner, but I bet
there's good reasons for it.
Totally unexpected to me though :)

>
> Bear newBear = bear1;
> newBear.claw = bear1.claw.dup;
>
> IIRC struct now have an opAssign that can burry this in the struct code rather than the client code.

Is opAssign available in D1.0?

> However this still has a few problems: 1, if claw contains a reference type you will now have 2 Claws that refer to the same thing and 2, you need to maintain opAssign to be sure that it copies all members.
>
> One way to attack both of these would be to use a template to build a generic deep copy that uses compile time refection to copy all the members, duping arrays of PODS, copying basic types and recursively deep copying reference types and arrays of them.
>
>

Guess I'll do that.
Thanks for the help!


January 03, 2009
nobody:
BCS:
> > However this still has a few problems: 1, if claw contains a reference type you will now have 2 Claws that refer to the same thing and 2, you need to maintain opAssign to be sure that it copies all members.
> >
> > One way to attack both of these would be to use a template to build a generic deep copy that uses compile time refection to copy all the members, duping arrays of PODS, copying basic types and recursively deep copying reference types and arrays of them.

> Guess I'll do that.
> Thanks for the help!

If you create a generic deep copy I may find it useful.

Bye,
bearophile
January 04, 2009
"bearophile" <bearophileHUGS@lycos.com> wrote in message news:gjn7s1$1vk4$1@digitalmars.com...
> nobody:
> BCS:
>> > However this still has a few problems: 1, if claw contains a reference type you will now have 2 Claws that refer to the same thing and 2, you need to maintain opAssign to be sure that it copies all members.
>> >
>> > One way to attack both of these would be to use a template to build a
>> > generic deep copy that uses compile time refection to copy all the
>> > members, duping arrays of PODS, copying basic types and recursively
>> > deep
>> > copying reference types and arrays of them.
>
>> Guess I'll do that.
>> Thanks for the help!
>
> If you create a generic deep copy I may find it useful.
>
> Bye,
> bearophile

Heh, unfortunately I wouldn't even know where to begin in order to make something like that.


January 04, 2009

nobody wrote:
> "bearophile" <bearophileHUGS@lycos.com> wrote in message news:gjn7s1$1vk4$1@digitalmars.com...
>> nobody:
>> BCS:
>>>> However this still has a few problems: 1, if claw contains a reference
>>>> type you will now have 2 Claws that refer to the same thing and 2, you
>>>> need to maintain opAssign to be sure that it copies all members.
>>>>
>>>> One way to attack both of these would be to use a template to build a
>>>> generic deep copy that uses compile time refection to copy all the
>>>> members, duping arrays of PODS, copying basic types and recursively deep
>>>> copying reference types and arrays of them.
>>> Guess I'll do that.
>>> Thanks for the help!
>> If you create a generic deep copy I may find it useful.
>>
>> Bye,
>> bearophile
> 
> Heh, unfortunately I wouldn't even know where to begin in order to make something like that. 

Off the top of my head, it wouldn't be terribly hard.

What you would need is a global 'ddup' (deep-dup) function.  The generic case would look something like:

T ddup(T)(ref T value)
{
    T result;
    foreach( i,f ; value.tupleof )
        result.tupleof[i] = ddup(f);
    return result;
}

You'd then have to use either specialisation or lots of static if's to account for reference types, allocating new memory as necessary.

Can't see any particular reason why you couldn't do it...

  -- Daniel
January 04, 2009
Daniel Keep wrote:
> Off the top of my head, it wouldn't be terribly hard.
> 
> What you would need is a global 'ddup' (deep-dup) function.  The generic case would look something like:
> 
> T ddup(T)(ref T value)
> {
>     T result;
>     foreach( i,f ; value.tupleof )
>         result.tupleof[i] = ddup(f);
>     return result;
> }
> 
> You'd then have to use either specialisation or lots of static if's to account for reference types, allocating new memory as necessary.
> 
> Can't see any particular reason why you couldn't do it...
> 
>   -- Daniel

You want ddup to be polymorphic. Let's say you have a class with no default constructor, or a class that starts listening on a socket with some default local port -- in the first case, you couldn't ddup it without some ugliness; in the second, you could ddup it, but you'd get an exception because the port's already in use.

It would suffice to have an IClonable interface, and to check structs for a ddup property.
January 05, 2009

Christopher Wright wrote:
> Daniel Keep wrote:
>> Off the top of my head, it wouldn't be terribly hard.
>>
>> What you would need is a global 'ddup' (deep-dup) function.  The generic case would look something like:
>>
>> T ddup(T)(ref T value)
>> {
>>     T result;
>>     foreach( i,f ; value.tupleof )
>>         result.tupleof[i] = ddup(f);
>>     return result;
>> }
>>
>> You'd then have to use either specialisation or lots of static if's to account for reference types, allocating new memory as necessary.
>>
>> Can't see any particular reason why you couldn't do it...
>>
>>   -- Daniel
> 
> You want ddup to be polymorphic. Let's say you have a class with no default constructor, or a class that starts listening on a socket with some default local port -- in the first case, you couldn't ddup it without some ugliness; in the second, you could ddup it, but you'd get an exception because the port's already in use.
> 
> It would suffice to have an IClonable interface, and to check structs for a ddup property.

Obviously, you'd need to have the class support it itself; but the original question was in regards to a struct deep copy.  :P

For reference, I've written a generic serialise/deserialise pair of functions that do, more or less, the same thing, so it can be done.

  -- Daniel