Copy Object to a New Object

BlitzMax Forums/BlitzMax Beginners Area/Copy Object to a New Object

therevills(Posted 2008) [#1]
Hi All,

Is there a command to copy one object to another object?

If I do this:

Type TFoo
  Field test$
End Type

Local foo:TFoo = New TFoo
foo.test = "FOO"

Local bar:TFoo = foo

bar.test = "BAR"

Print foo.test
Print bar.test


Both prints out "BAR"... as it is coping by reference. I want both objects to be a different instance of each other, whilst keeping all the data.

Please help.

Cheers!


TaskMaster(Posted 2008) [#2]
You have to do the copy yourself.

Make a new object and copy over all of the fields.

There may be a way using reflection to get an object to copy itself, but I have not played with it yet.


therevills(Posted 2008) [#3]
Thanks TaskMaster...

Looking at my code again, and I'm now not sure what the problem is, I thought it was this.

Ive got a set of buildings each with a set of slots, my problem is that if I click on one building it sets the slot of each building with that type.

Eg 3 buildings, 2 houses and 1 bar. If I click the first house, the slots in both houses are used...!?!? I just want the building I clicked on!

Here is a simplified version of my code:



I thought of adding the following to TBuildingInfo:

	Method clone:TBuildingInfo()
		Local b:TBuildingInfo = New TBuildingInfo
		b.name = name
		b.slots = slots
		b.buildinginfoList = buildingInfoList
		Return b
	End Method


and assigning like this:

Local houseInfo:TbuildingInfo = infoList.Find("HOUSE")
b.info = houseInfo.clone()


But I still have the problem... any ideas?

Thanks!


therevills(Posted 2008) [#4]
Okay I think I found my problem... and it was that one object (TSlot) was still getting copied by reference...

I did this in TBuildingInfo:

	Method clone:TBuildingInfo()
		Local b:TBuildingInfo = New TBuildingInfo
		b.name = name
		b.slots = New TSlotList
		For Local s:TSlot = EachIn slots
			Local sl:TSlot = b.slots.clone(s, slots)
			b.slots.AddSlot(sl.x,sl.y)
		Next		
		b.buildinginfoList = buildingInfoList
		Return b
	End Method


And added clone to TSlotList:

	Method clone:TSlot(s:TSlot, sl:TSlotList )
		Local ss:TSlot = s.clone(sl)
		Return ss
	End Method


Finally added clone to TSlot:

	Method clone:TSlot(sl:TSlotList )
		Local s:TSlot = New TSlot
		s.x = x
		s.y = y
		s.empty = empty
		s.slotList = sl
		Return s
	End Method


The working code:



This is all when and good for objects with a small number of fields, but my proper TBuildingInfo has 40 fields ATM and will be more soon!

Is there a smaller/simpler way to do this?


therevills(Posted 2008) [#5]
I'm back again!

Found this post http://www.blitzbasic.com/Community/posts.php?topic=73573#822043 by Azathoth and it looks like it might work...

I guess this is how you use the metadata tags(?):

Type TBuildingInfo
	Field name$ {Clone}
	Field slots:TSlotList{Clone}
	Field buildingInfoList:TBuildingInfoList {Clone}
End Type


And assign them by:
b = buildings.AddNew(100,100, "HOUSE")
Local houseInfo:TbuildingInfo = infoList.Find("HOUSE")
b.info = TBuildingInfo(CloneObject(houseInfo))


This looks like it works for the name$ field, but still slots:TSlotList is still by reference... any ideas?

Cheers


Czar Flavius(Posted 2008) [#6]
I think there might be a way to memcopy the objects directly, but I'm not sure how to do this.


Brucey(Posted 2008) [#7]
I think there might be a way to memcopy the objects directly

Not advisable, me thinks.


TaskMaster(Posted 2008) [#8]
You have to create a new slot list, and re-add each object in the list (if you want the same objects in the list). If you want the objects in the list to also be copies, you will have to copy them as well.


Brucey(Posted 2008) [#9]
Yes. There are 2 ways you can think of copying :

A basic copy, which in effect creates a new objects and populates it with the objects the original contains. Those objects will be the same instances.

A deep copy, which will create new copies of ALL objects. If you have a TList field, it would create a new TList, and new instances of all objects inside that list.

The second form is obviously a lot more intensive, since it potentially has a lot of work to do.
It all depends how much you need copied? Just the object you are dealing with, or all the objects it uses?

My persistence module can do "proper" deep copying, albeit in a roundabout way - it converts the object-structure into XML, which you can then de-serialise to get a whole new set of objects. (i.e. it's designed for snap-shots of your objects for persisting on Disk, rather than on-the-fly copying).


therevills(Posted 2008) [#10]
Thanks for the replies guys.

@Czar Flavius - Ive read that doing the memcopy can really mess up the GC, especially if you have got objects within objects.

@TaskMaster, looks like your right... Azathoth CloneObject function doesnt like to copy the TSlotList field (I kept getting null pointers!? when trying to {Clone} it)...so here is what Ive come up with:

TBuildingInfo (notice the NoClone on the lists)

Type TBuildingInfo
	Field name$ {Clone}
	Field slots:TSlotList{NoClone}
	Field buildingInfoList:TBuildingInfoList {NoClone}
	
	Method clone:TBuildingInfo()
		Local b:TBuildingInfo =  TBuildingInfo (CloneObject(Self))
		b.slots = New TSlotList
		For Local s:TSlot = EachIn Self.slots
			Local ss:TSlot  = s.clone(slots)
			b.slots.AddSlot(ss.x,ss.y)
		Next
		b.buildingInfoList = Self.buildingInfoList
		Return b
	End Method
End Type


And to create the clone:

Local buildings:TBuildingList = New TBuildingList ' create the global building list
Local b:TBuilding
b = buildings.AddNew(100,100, "HOUSE") ' create a new building and add it to the list
Local houseInfo:TbuildingInfo = infoList.Find("HOUSE") 'get the data for the building
b.info = houseinfo.clone() ' create a new copy of the data and assign it to the new building
b.info.name = "1st House" ' check to see if the clone has worked


Here is the full code:



@Brucey, thanks for the info - Ill have a look at your persistence module - sounds interesting...


Azathoth(Posted 2008) [#11]
@TaskMaster, looks like your right... Azathoth CloneObject function doesnt like to copy the TSlotList field (I kept getting null pointers!? when trying to {Clone} it)
I think that has to do with {Clone} only affecting that specific field and not TSlotList's fields (it extends TList which doesn't contain the {Clone} metadata).

Edit: This was to prevent lock ups when trying to clone cyclic references.