Copying objects
BlitzMax Forums/BlitzMax Beginners Area/Copying objects
| ||
Ugghhh......after a mammoth bug hunting session I've found my problem, but I need a more elegant solution. I have a type called TBlock, now within TBlock is another type called TRenderObject and within TRenderObject are around 30 fields to do with animation and drawing, kind of like this: Type TBlock Field renderObject :TRenderObject etc endtype Type TRenderObject Field identifier :String Field x :Float Field y :Float Field image :TImage Field priority :Byte Field objectType :Short Field drawFlag :Byte etc..... endtype Now I have a method which copies a TBlock Object which is the following: ...and is called like this: BlockCopy:TBlock = block.Copy() ...and works great, except that it doesnt copy the contents of the renderObject field. Now I can sort this by using a similar technique of assigning fields as I have done in the above example, but as TRenderObject has so many fields I wondered if there was a more elegent solution rather than going through each field (as I have done in TBlock.copy() and assigning them manually...? Sorry if this explanation seems a little disjointed, blurry eyes and blurry mind. Thanks for any help |
| ||
The best way is to do it in OO style: Add the method copy to all of your objects and call it for all child object on copying :-) (although I would have called it clone or deepclone as you clone the linked part as well. copy would normally not copy the child data but reference it only as you do in the above code) |
| ||
I guess this is the way I thought, I'm just concerned that if I do this and later update the type, I'll forget to add any additional fields I add to the copy! Would there be any way to check this? I dont know if this would work, but maybe I could do a sizeof TRenderObject and assert if that is different size to the copy I make - then I would know that I am missing fields in my copy method....I'll give this a try. |
| ||
The only way is always updating the copy method after modifiying fields. Sizeof would be a possibility, but only as long as you don't have lists or the like in |
| ||
Hmmm...it doesnt have lists, but there is a problem. Iam adding an additional float field to the type, but not the copy method. (so there is a field which is not getting copied across). But when I look at the size of the TRenderObject (with the extra field) and an instance of it created using the copy method, they are the same....adding the extra field doesnt seem to have done any good - its reporting back as being of equal size. |
| ||
I'm guessing an object isn't created in contiguous memory so you can't do a memcopy?Type ttest Field x End Type my:ttest = New ttest my.x = 5 mynew:ttest=New ttest MemCopy(Byte Ptr(mynew),Byte Ptr(my),SizeOf my) Print mynew.x <edit> Seems OK... Type ttest Field x Field y Field name:String End Type my:ttest = New ttest my.x = 5 my.y = 51 my.name="Hello World!" mynew:ttest=New ttest MemCopy(Byte Ptr(mynew),Byte Ptr(my),SizeOf my) Print mynew.x + " " + mynew.y + " " + mynew.name but can we be sure? Could it mess up inherited types? Type base Field g Field h End Type Type ttest Extends base Field x Field y Field name:String End Type my:ttest = New ttest my.x = 5 my.y = 51 my.g = 12 my.name="Hello World!" mynew:ttest=New ttest MemCopy(Byte Ptr(mynew),Byte Ptr(my),SizeOf my) mynew.h = 999 Print mynew.x + " " + mynew.y + " " + mynew.name + " " + mynew.g + " " + mynew.h seems to work as well. |
| ||
I don't have problems using byte ptr to copy. |
| ||
Ohh. memcopy is a better choice |
| ||
Quick check and other posts suggest the problem is with the garbage collector. Not sure why as we've done a 'new' so should be known by GC. Test suggests listremove and null on mynew will release memory. |
| ||
I usualy hate to get involved in language discussions, but some some kind of deepcopy function would come in real handy I think. NewObject:tBlar = Copy OldObject. Oh, and some buit in serialisaton for easy loading and saving. Both of these things required to to step through each of your objects and account for every field. This becomes dificult to maintain and prone to bugs as you game grows. |
| ||
The problem with sizeof btw is that your object actually has the same size. just because you didn't copy over a float value, it does not mean it is not there ... its only 0. and memcopy is definitely no possibility or you will see more MAVs than you can defeat ... memcopy and the like is not GC handled nor are their result ... |
| ||
No I did actually I added a float and assigned a value to it...yet its still coming out as the same size. I''ll look at it again... [Edit] Ah...ok sorry, see your point....so size of comparison is useless in this situation then? |
| ||
Yes but the problem is that an instance of a class (object) will ALWAYS have the same size as the class. so sizeof is of no use. The moment you do "new childclass" it already has the size, no mather if you copy over the float value. Its space is reserved due to the type declaration. Thats what I meant. |
| ||
Why would the sizeof be a problem? The sizeof the class is calculated from the sum of it's fields. e.g. Type ttest1 Field x End Type Type ttest2 Field x Field y:Float Field z:String End Type Print SizeOf ttest1 + " " + SizeOf ttest2 my:ttest1 = new ttest1 my.x=5 print sizeof myThis is reserving all the space we might need and the sizeof the object will never increase. I have run a test and the 'mynew' is picked up by the GC... I also have the feeling that something will go wrong at some point but I can't see what. |
| ||
Yeah the size of an object will never increase. But SizeOf is no solution to track if the copy method does not copy a field as the space for the field is reserved no mather if it is filled or not. You somehow got quite off the topic somehow ;-) On the what: The problem is, that if your object you copy has references to objects and not only numerics, it will fail. Because memcopy a reference does not raise the reference count. So if all "regular created references" (using new) are away, the objects your copy references to will simply disappear. And it will be nearly impossible to specify this bug if you are not aware of that risk. MemCopy thought might work for Shallowcopy on numeric only types. But I would not use it just for inconsistency reasons and the risk that the GC might change on handling byte ptr etc. |
| ||
if the copy method does not copy a field as the space for the field is reserved no mather if it is filled or not. Sorry Dreamora. I am sure it's right I just don't understand what you're saying. If the object isn't held in a single contiguous area of memory there'll be a problem which was one of my earlier questions. I think I see what you mean about referencing other objects. If, for example, my:TTest has a field otherobject:TTest2 then it's possible that object might be deleted as it does not know my:ttest2 is referencing it. OK. I think that's worth a quick test. <edit> The result... <edit> Added extra gccollect and it does fail for exactly the reason Dreamora says. No reference for ttest2 so it was deleted although mynew needed it. |
| ||
The objects are in a continous area of memory. Sorry missed that question. A BM object is a C++ object with 2 hidden functions (constructor and destructor) so for copy of only numeric objects like TVector or TMatrix, the memcopy method should work. |
| ||
in that case we need to do 2 copiesMemCopy(Byte Ptr(mynew),Byte Ptr(my),SizeOf my) MemCopy(Byte Ptr(mynew.test2),Byte Ptr(my.test2),SizeOf my.test2) |
| ||
This will totally break with the GarbageCollector. BM is not meant for manual memory operations. They are only provided so you can use C / C++ imports which need continous memory blocks for data you send. For example for arrays and the like. |
| ||
The structure after the memcopy was fine (even the object reference) but, if the object pointed to by my.test2 was deleted (and was the last *known* reference) mynew.test2 would point to a non-existent object. Does anybody know how the list of 'referenced objects' is maintained? Is it possible to manually add a reference from mynew.test2 to the object and increment it's counter? (I'm guessing it is a simple counter). |
| ||
Check struct BBObject in the brl.blitz blitz_object.h . It has an int that counts the refs. So at best you would do a clone functionality on that level by adding it to that file or a similar way. You would be able to do from BM as the struct is a "super object" to the actual BBObject class implementation which results in :Object within BM. |
| ||
I guess there's no way of checking which fields are object references and creating a dummy version to manually increment the counter. On deleting the clone also delete the dummy object? |
| ||
Not really. Especially as out of OO sight, the implementation of the Copy / Clone method and recursively calling it is the correct way to go. Especially in a managed environment that needs to be used properly or can cause real problems. The "what if I miss to add a new field to copy/clone method" might happen but it is debugged extremely fast if it happens. so no real problem |