Managing External Objects

BlitzMax Forums/BlitzMax Programming/Managing External Objects

Gabriel,

I slept on you question and this is what I came up with. Bascially it creates intermidiary layer that does the reference counting. Kinda fun little coding over morning coffee. I tried to comment it enough to make sense.

SuperStrict

Rem 
  TExternal is only to simulate an externally managed object that can
  be returned multiple times per function call.  We are just going to store 
  objects in simple stringlist,  nothing fancy or fast

  Our handle is nothing more than memory address of the first element of data in the 
  object.  it will be consistent for all instances pointing to this object.  


End Rem

Type TExternal
   	Field _Text:String    
   	Method Handle:Int()
		Return Int(Byte Ptr(Self))
   	End Method
End Type

Global ExternalObjects : TList=New TList


Rem
  A function that gets external handle for string in string list
  function will return handle to same item if already exists
  
End Rem

Function GetExternal:Int(Text:String)
  For Local e:TExternal=EachIn ExternalObjects
	If e._Text=Text
  	    Print "Reusing "+Text
		Return e.Handle()
	End If
  Next  
  Local newitem:TExternal= New TExternal
  newitem._Text=Text
  ExternalObjects.AddFirst(newitem)	
  Print "Adding "+Text
  Return newitem.Handle()
End Function

Rem 
  A Function to Delete the object when we are done with the handles and truely want
  to delete the object  
End Rem

Function DeleteExternal(Handle:Int)
  For Local e:TExternal=EachIn ExternalObjects
	If e.Handle()=Handle
	   ExternalObjects.Remove(e)
	   Print "Deleting "+e._Text
	   Return
	End If
  Next  	
  Throw "Unable to find item to delete"
End Function


Rem
  Test to see if our external string to handle works  this just test the idea
  of external object manager than returns handles more than  once if item 
  already exists in collection or adds it if it doesnt
End Rem

'Local a:Int=GetExternal("APPLE")
'Local b:Int=GetExternal("CHERRY")
'Local c:Int=GetExternal("APPLE")

'Print "a="+a
'Print "b="+b
'Print "c="+c

'DeleteExternal(a)
'DeleteExternal(b)
'DeleteExternal(c) ' This will throw since item does not exists


Rem 
	Here is the implementation to manage this.  It uses a TMap with a special Type to manage
	reference counting of the instance of the Handle given out.
	
	When that count reachs zero it will remove object from the map 
	and delete the external object


End Rem



Rem
	This object is used to manage the handles we have already gotten
	with a TMAP.  Since TMAP requires object we can make it use 
	the key and behavior by overriding the Compare Method
End Rem


Type THandle
	Field _handle:Int

	Method Compare:Int(o:Object)
		Return _handle-THandle(o)._handle
	End Method
		
	Function Create:THandle(Handle:Int)
		Local	t:THandle
		t=New THandle
		t._handle=Handle
		Return t
	End Function
End Type

Rem
	This is the Type we use to reference count the external items	
End Rem

Type TExternalMapItem
	Global _Map:TMap= New TMap
	Field _Handle:Int
	Field _RefCount:Int
		
	
	Method AddRef()
		_RefCount:+1
	End Method
	
	Method ReleaseRef()
		_RefCount:-1
		If _RefCount= 0
			DeleteExternal(_Handle)	
			_Map.Remove(THandle.Create(_Handle))
			_Handle=0
		End If
	End Method
	
	
	Function GetExternalMapItem:TExternalMapItem(Text:String)
	    ' create a Thandle 
		Local key:THandle= THandle.Create(GetExternal(Text))
		' if the Thandle is in the map we just add a reference
		' otherwise we insert it in map and add reference
		Local mapitem:TExternalMapItem=  TExternalMapItem(_Map.ValueForKey(key))
		If (mapitem=Null)
			mapitem=New TExternalMapItem
			mapitem._handle=key._handle
			_Map.Insert(key,mapitem)
		End If
		mapitem.AddRef()
		Return mapitem
	End Function				
	
End Type


Rem
	This is the wrapped item.  we implement a Dispose method if we want to
	ensure deletion before Delete is called since we cant control when the GC
	will clean up this takes care safely wacking it, under our control or GC control.
End Rem


Type TExternalWrapper
	Field _ExternalMapItem:TExternalMapItem
	Method Handle:Int()
		Return _ExternalMapItem._Handle
	End Method
	Method Dispose()
		If _ExternalMapItem _ExternalMapItem.ReleaseRef()
		_ExternalMapItem=Null
	End Method
	Method Delete()
		Dispose()		
	End Method	
	
	' our Create function for the wrapper
	Function Create:TExternalWrapper(Text:String)
		Local wrapper:TExternalWrapper= New TExternalWrapper
		wrapper._ExternalMapItem=TExternalMapItem.GetExternalMapItem(Text)
		Return wrapper
	End Function    
End Type



Global a:TExternalWrapper=TExternalWrapper.Create("APPLE")
Global b:TExternalWrapper=TExternalWrapper.Create("CHERRY")
Global c:TExternalWrapper=TExternalWrapper.Create("APPLE")

Print "a's Handle ="+a.Handle()
Print "b's Handle ="+b.Handle()
Print "c's Handle ="+c.Handle()
b=Null
a=Null
Print "Before GCCollect Only CHERRY should be released"
GCCollect()
c=Null
Print "Before GCCollect Only APPLE should be released"
GCCollect()
Global d:TExternalWrapper=TExternalWrapper.Create("PEAR")
Print "d's Handle ="+d.Handle()
Print "Force Deletion"
d.Dispose()
Print "Gone!"
d=Null
Print "Before GCCollect nothing will happen"
GCCollect()
Print "See nothing happend since we already disposed"
Print "Done"


Maybe it will help, good luck

Doug Stastny