www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Struct copy, how?

reply "nobody" <somebody somewhere.com> writes:
(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]);
}
Jan 02 2009
parent reply BCS <ao pathlink.com> writes:
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.
Jan 02 2009
parent reply "nobody" <somebody somewhere.com> writes:
"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!
Jan 02 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Jan 03 2009
parent reply "nobody" <somebody somewhere.com> writes:
"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.
Jan 03 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
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
Jan 04 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
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.
Jan 04 2009
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
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
Jan 04 2009