Flag an instance "dirty" on field-change?
BlitzMax Forums/BlitzMax Programming/Flag an instance "dirty" on field-change?
| ||
Hi, I'm working on a tree-structure based on node-/leaf-types (where each node has a list for the 0..n subnodes as usual) This tree is to be replicated elsewhere (by DBs/Network) but I only want to send information about the nodes that have actually changed, since last update, due to bandwidth etc. So i.e. I want to flag the changed nodes as "dirty" (as in dirty cache elements) as automatically as possible. This can be done in these two ways (that i know of): 1) Use reflection on each update (as in 1-per-loop update()-function) and check if something changed against some "old state" or checksum, on each node in the whole tree. (very CPU-time wasting method) 2) Use setters on all fields exclusively and set the dirty flag as soon as some user of this node/leaf updates a value through the setter. (Places responsibility on the implementer of eventual sub-types, to the node/leaf types, to handle the dirtyflag on his/her own.) Can anyone come up with any other ideas how this could be done better/faster? edit: clarification |
| ||
I would go with the setter way. Also, there's no strain on the implementer of a sub-class. You can either handle this in a hidden or final method in your class, or the implementer could simply call "Super.[method]" when overwriting a method. Hope this helps :) |
| ||
Hmm, I'll try to illustrate the "problem" with a little code.'This is my base class Type TTreeNode Field dirty:int 'lots of other cool stuff here End Type 'This is a derived class made by someone else Type TPlayer Extends TTreeNode Field x:int Field y:int End Type Now that "someone else" that uses his own TPlayer class still has to do this, to correctly mark TTreeNodes as "dirty", when changing values in an inherited instance: 'This is a derived class used by someone else Type TPlayer Extends TTreeNode Field _x:int Field _y:int method setx(value:int) _x=value dirty=True end method method sety(value:int) _y=value dirty=True end method End Type While this is viable its not very convenient and a bit error prone (if there are lots of subclasses and fields the subclassing user might just forget to set dirty at some field and the update breaks) Other languages like java as an example has "call proxies" that can intercept all calls, and could be used for to manage something like this internally in the base-class, and thats why I'm asking if anyone knows of a mechanism in BlitzMax that could be used for something similar. Is there a better way to do this, or should I just go for the setter method? |
| ||
Although it's not ideal, the only other way I can think of is to have a TTreeNode.Update function/method that you tell the implementer of sub-classes they must call when finished making changes, eg.Type TTreeNode Global dirty Function Update () TTreeNode.dirty = True End Function End Type Type TPlayer Extends TTreeNode Field x Field y End Type t:TPlayer = New TPlayer t.x = 100 t.y = 100 t.Update Print t.dirty At least they don't have to implement their own methods for each extended type this way, just remember to call the function. I'm not a real OO programmer, though, so this is just a system that seems easier to me! |
| ||
If you're doing dirty/clean just use bitmasks - saves a lot of ram. |
| ||
I don't see anything wrong with James' code, but to add another option to your list, you could lock and unlock your objects..Type TTreeNode Field locked%, dirty% Method Lock () locked = True End Method Method Unlock () locked = False dirty = True End Method End Type Type TPlayer Extends TTreeNode Field x#, y# Method setx ( pos# ) Assert( locked ) ' you could enforce locking in debug build If locked x = pos ' you can prevent change if object not locked End Method End Type It still can't prevent direct access, but that's a bmax limitation. |
| ||
I'll have to tinker around a bit more and do some speed-tests to see what I can do about this :) I'll post back if I find a worthwhile solution that is good enough to share. Thank you all very much for the input! |
| ||
I completely forgot about Abstract -- you could easily force them to define the Set/Get methods when defining extended types:Type TTreeNode ' Whatever... Method SetX (value) Abstract Method SetY (value) Abstract End Type |
| ||
If you don't mind to have one extra abstract layer in between see the example below: Only the the main SetX() method does set the Dirty Flag, thus transparent to the user. |
| ||
You could probably use reflection as well without much CPU time wasted (I think).Type TTreeNode Field dirty:Int Method SetInt(fld:String, val:Int) dirty = True Local tid:TTypeId = TTypeId.ForObject(Self) tid.FindField(fld).SetInt(Self, val) End Method EndType Type TPlayer Extends TTreeNode Field X:Int Field Y:Int End Type Local p:TPlayer = New TPlayer p.SetInt("X", 10) p.SetInt("Y", 15) DebugLog(p.X + " , " + p.Y) There's the downside of having to specifically handle all types (int, float, string, object, etc.) |
| ||
I completely forgot about Abstract -- you could easily force them to define the Set/Get methods when defining extended types: Yes, but that completely mitigates what I'm trying to accomplish. I probably wasn't clear enough on my purpose: I wanted to make a TTreeNode that can be extended to any use by another developer aswell i.e. that another dev wants for example TPlayer, TBag, TItem (that in turn is extended to TWeapon, TAmmo ...) etc. each with their own properties for his game. It's impossible for me writing the base-class to foresee what the possible fields that will be necessary for another dev in the future. The TPlayer might need x, y. The Item might require damage, speed etc. The TAmmo might have HitAccuracy etc. (not mentioning the fact that being forced to implement a speed-setter on a TChair might not be what everyone wants in their game) What I'm leaning to right now is to make a solution where setting dirty on all setters is the speedy, preferred way, but with a fallback solution to a reflection/checksum based "dirtyflagging". I did something similar in my MaGNet a while ago: http://blitzmax.com/Community/posts.php?topic=81799 There the dirty flag was called "modified" The difference between MaGNet and this "Distributed Tree" is that I want the new Tree to be able to service a much larger player-base (with "limited node sight" for clients etc.). Larger player-base means lots and lots of nodes so processing time is not trivial anymore :) |