Sunday 20 September 2009

BBObject* <> QVariant

All list widgets are generally able to store user data with each item. This usually helps the user keep track of how a particular item relates to their own data model - for example, you might have a TItem type which holds some useful information that you'd rather the widget tracked instead of you having to create your own TList and manually match up a list item to the item in the TList.

First, a quick note about how objects and the GC are handled in BlitzMax.
Each object instance has a form of reference counting attached to it. As objects lose their references (i.e. nothing is using it any more), they are eventually removed from the system.
If we store the object in a BlitzMax container (basically anywhere BlitzMax controls assignment of an object), all is well. But, outside of BlitzMax, we must manually keep track of object references by first retaining, and later releasing that object. When an object has no more references, it will be flagged for deletion, and at some point, removed by the GC.

Qt uses QVariants to hold user data, which is great for numbers and strings, but not so useful for BlitzMax Objects. QVariants are stored on the stack, and therefore, are memory-managed by C++ itself. This is a problem if we want to store a BlitzMax object in one, because there's no trigger to tell us that the QVariant object is going to be removed - at which point we can release our own object.

So I came up with an alternate solution in which each list type widget also contains a map of related user-data. Each entry in this map has a unique Long identifier. It is this identifier which is store in the widget item user-data. On request, the id is retrieved and looked-up against the map, which retrieves the appropriate object.
Whenever an item is removed, the map also requires an update to remove any mapped user data from it.
On deletion of a widget, the user-data is automatically un-referenced because the item map will be flagged for collection, and therefore also its contents.

This seems to work well in the few widgets that I've implemented so far.
For something like a table, I may have to introduce a slightly more complex alternative, but hopefully it will hold up there too.