How does Handle() work exactly? + linklist stuff

Blitz3D Forums/Blitz3D Programming/How does Handle() work exactly? + linklist stuff

Shifty Geezer(Posted 2005) [#1]
Just tried some experiments and I'm finding this a wierd function. I have an array of objects I'm gonna replace with a linked list. As per my previous thread, I've a subtype object within another type.

TYPE dog
Field stuff
END TYPE

TYPE cat
Field pet.dig
ENDTYPE

With an array of 50 cats, I have 50 dog sub-objects too. Now after creating all this objects I text certain values...

a=Handle(cats(5)\dog)
b=Handle(cats(6)\dog)
c=Handle(cats(7)\dog)
Text 10,20,a
Text 10,40,b
Text 10,60,c

Regardless of which index I use, a,b and c are always = 1,2,3 respectively.

Does Handle() record the object's location in an internal array? That seems to be the case. And object.xxx() returns an object so stored in this Handle() array? There's no direct access to the memory location of an object, but all objects can be written to this handle array.

For me to construct a linked list of, given the above example, cats, I would have to use Handle() on every cat object I create. I could point each cat object to others using a .cat field and object.xxx(). What happens when a cat object is deleted? Let's say I have 50 cats, and Handle() them all. Now I delete the third cat I created. What happens to the Handle() index 3? What is I now create another cat? Does it fill the hole or get tagged on the end?

From the sounds of it it's only really n array much like my existing array. If there's no direct pointers a true linked list doesn't seem possible. Ultimately I'm wanting a list of objects where I can add and delete at will from the collection, with some creation creating several concurrent objects that need to be kept together. Inserting into a linked list is ideal. At the moment I need searching thorugh the array for the first available space large enough for the objects I'm adding, which is far from elegant!


Ross C(Posted 2005) [#2]
Handle creates a handle to a type object. The number returned probably references a database or list that blitz has built itself. So, using the object command, your telling blitz to return the handle store in the xth place in the list blitz has built.

If you delete a type object that you are storing a handle of, then try to use the object command to return to it, you'll get an object does not exist error. Only when you try and access a type field or delete the type object though.

Simple example:

If you try and delete an object, then select it again, error pops up.


Dim cow_handle(7)

	Type cow
		Field name$
	End Type


	c.cow = New cow
	c\name$ = "alf"
	cow_handle(0) = Handle(c.cow)
	
	c.cow = New cow
	c\name$ = "dogzer"
	cow_handle(1) = Handle(c.cow)
	
	c.cow = New cow
	c\name$ = "jimbob"
	cow_handle(2) = Handle(c.cow)
	
	c.cow = New cow
	c\name$ = "agnus"
	cow_handle(3) = Handle(c.cow)
	
	c.cow = New cow
	c\name$ = "johnny"
	cow_handle(4) = Handle(c.cow)

	c.cow = New cow
	c\name$ = "john"
	cow_handle(5) = Handle(c.cow)
	
	c.cow = New cow
	c\name$ = "jim"
	cow_handle(6) = Handle(c.cow)
	
	c.cow = New cow
	c\name$ = "peter"
	cow_handle(7) = Handle(c.cow)

number% = Input(" enter a cow number to delete between 0 and 7 - ")

	c.cow = Object.cow(cow_handle(number))

	Print c\name$

        Delete c.cow

number% = Input(" enter a cow number between 0 and 7 - ")

	c.cow = Object.cow(cow_handle(number))

	Print c\name$



octothorpe(Posted 2005) [#3]
Does Handle() record the object's location in an internal array?

What happens to the Handle() index 3? What is I now create another cat? Does it fill the hole or get tagged on the end?


Yeah, Blitz keeps track of handles in its own array or something. The holes don't seem to get filled.

But all of this is academic: it shouldn't matter to you, since you can't do anything with Handles except pass them to Object() and test them for equality. Handle() might as well be returning you a real pointer... as long as you don't want to do pointer arithmetic.

For me to construct a linked list of, given the above example, cats, I would have to use Handle() on every cat object I create. I could point each cat object to others using a .cat field and object.xxx().


If your next_node field is of type cat, you wouldn't need Handle() at all. You can construct a fully featured linked list without using Handle(). Here's the basic idea:
Type foo
	Field next_node.foo
End Type

f1.foo = New foo
f2.foo = New foo
f1\next_node = f2
The only reason you should need Handle() is if you want to keep references to objects of different types.
Type ferret
	Field colour$
End Type

Type emu
	Field bites
End Type

Type person
	Field pet
	Field pet_class$
End Type

p.person = New person
e.emu = New emu
e\bites = True
p\pet = Handle(e)
p\pet_class$ = "EMU"
go_for_a_walk(p)

Function go_for_a_walk(this_person.person)
	If this_person\pet_class$ = "FERRET" Then
		this_ferret.ferret = Object.ferret(this_person\pet)
		Print "Your "+this_ferret\colour$+" ferret escapes!"
		Delete this_ferret
		this_person\pet = 0
		this_person\pet_class$ = ""
	ElseIf this_person\pet_class$ = "EMU" Then
		this_emu.emu = Object.emu(this_person\pet)
		If this_emu\bites Then
			Print "Your emu bites you!"
		Else
			Print "Everything is swell."
		EndIf
	ElseIf this_person\pet_class$ = "" Then
		Print "Your loneliness causes you to sink deeper into depression"
	Else
		RuntimeError "invalid this_person\pet_class$ = " + this_person\pet_class$
	EndIf
End Function
This example may seem trivial (why not just use a field for each pet type - emu.emu and ferret.ferret) but consider this:

The problem with designing a linked list which manages objects of type foo is that you'll be hard-coding all your functions to deal with type foo. If you wind up needing a second linked list for objects of type bar later on, (or worse, a second linked list of objects of type foo!) you'd need to copy all your functions. Handle() to the rescue! If you write all your linked list code to manage a list of integers instead of type objects, then you can store Handles in there and turn them back into Objects when you retrieve the values.

This example uses my double linked list library.

include "listType.bb"

; create some objects
type fooType
	field value$
end type
x.fooType = new fooType : x\value$ = "bar"
y.fooType = new fooType : y\value$ = "baaz"
z.fooType = new fooType : z\value$ = "quux"

; push them into a new list
list.listType = new listType
list_push(list, handle(x))
list_push(list, handle(y))
list_push(list, handle(z))

; iterate over our list
list_iterator_begin(list)
while list_iterator_next(list)
	this_element.fooType = object.fooType(list_iterator_get(list))
	print this_element\value$
wend
You use Handle() when you put things into the list, and Object() when you take them out. The linked list only has to worry about integers, and you get to store whatever you want in there. The only drawback is that it's up to you to make sure that you don't mix types.


Techlord(Posted 2005) [#4]
Many folks insist on using Handle/Object commands. I do not. Avoid them like the plague. There is a reason why they are not documented.


Ross C(Posted 2005) [#5]
It's really up to what you need. I wouldn't say avoid like the plague. they have their uses, and in some cases are extremely useful


Techlord(Posted 2005) [#6]
It's really up to what you need.


This is true. And...

You can create your own Object Management System which is much more flexible and faster.


octothorpe(Posted 2005) [#7]
Using Handle() and Object() is the only way you can achieve polymorphism in Blitz Basic.

There's plenty of reading material available on the pros and cons of container classes, polymorphism, and object-oriented code. Unfortunately, this has been a "religious debate" for quite a while now and there's a lot of people out there who have made up their minds without weighing all the evidence; there's also a lot of reading material out there which is opinion-heavy and reasoning-light.


Techlord(Posted 2005) [#8]
Using Handle() and Object() is the only way you can achieve polymorphism in Blitz Basic.


I would like to see an example that. I dont see how thats possible when you have to declare the type extension when using the Object command (ie:Object.ferret) when passing a Handle. Don't get me wrong, I'm not being argumentive, but, would honestly like to see and example of that.

From what I gather the only advantage to Handle/Object commands is random access to a object in type collection rather than using a For...Loop to evalute the collection and select a specific object. Random access can be achieved easily with a Array of Types and a ID Field within the type.


DH(Posted 2005) [#9]
Many folks insist on using Handle/Object commands. I do not. Avoid them like the plague. There is a reason why they are not documented.


What is the reason?

I use them and depend on them.. coding my own object management system is going to produce tons of overhead when i can merely use object/handle to control how I refference everything.


Techlord(Posted 2005) [#10]
DH,

The overhead is small and well worth it for control. Only Mark Sibly knows whats going on behind the scenes with the Object/Handle commands. I'm sure there is some overhead involved considering its slower than an Array of Types.


Shifty Geezer(Posted 2005) [#11]
Yes, I don't understand Object() either. When I heard about Object() I thought it returned an object of the type held in Handle(), but in reality you have to declare the type of Object(). So storing cats, dogs and ferrets, you need object.cat(), object.dog() and object.ferret() to read them, and you need ot know what type you've written to which Handle() index. If I write

a=Handle(pussy.cat)
b=Handle(puppy.dog)
c=Handle(mink.ferret)

I need to call Object.cat(a) but Object.dog(b), instead of Object(a) and Object(b) and get a type returned of whatever type a and b are. That would make Object() a very useful function, but as you need to declare its type it's no different really to using custom objects for selecting from list/arrays of objects.

Anyhow, thanks octothorpe for the insight. Looking at you linked list I realised it's using type fields to store the pointers. This is ideal for me - I don't need complicated lists, so I'll just add the list fields into my object class as list functions are a doddle to write.


DH(Posted 2005) [#12]
Consider the following:

You have a huge list of gadgets for a gui. Each one is held in the same type list.

So
Type Gadget
field X,Y
End Type


Now, the user wants to create a gadget. Here is what we do to make it easy!

Function CreateGadget(X,Y)
local G.Gadget
G=new Gadget
G\X=X
G\Y=Y
return handle(G)
End Function


So the user creates his gadget using the following:
MyGadget = creategadget(5,5)


Now we can refference their object easily by its handle!
So if the user wants to change the object x and y with the following function

Function Position(GAD,X,Y)
local G.Gadget
G=object.Gadget(Gad)
if G<>null then
G\X=X
G\Y=Y
Endif
End Function


Now the user can change the properties, and we easily refferenced which object they wanted to change by lettting them do this
;Changing my gadgets position
Position(MyGadget,15,15)


So we can easily get which object is being refferenced by merely refferencing its handle.

Also consider the following. What if you filled the screen with cubes and wanted te user to select a cube. How would you know which cube he selected? You can create a list of cubes and cycle through each one seeing it it's entity was the picked one. But that takes waaaaayyyy too long!

You could easily refference each entity with the handle of the type like so
Type cubelist
	field Cube
end type

Graphics3d 800,600,16,2
setbuffer backbuffer()
cam = createcamera()
for a = 1 to 500
	makeCube(rnd(-100,100),rnd(-100,100),rnd(1,1000))
next
While not keydown(1)
	if mousedown(1) then
		ent=camerapick(cam,MouseX(),MouseY())
		GetSelected(Ent)
	endif
	renderworld
	flip
wend
end

Function MakeCube(X,Y,Z)
	local C.CubeList
	C.Cubelist = new cubelist
	C\Cube = createcube()
	Positionentity(C\cube,x,y,z)
	entitypickmode(C\Cube,2)
	;Name the entity with our handle
	nameentity(C\Cube,handle(c))
end function

Function GetSelected(Ent)
	local C.CubeList
	C=object.cubelist(entityname(ent))
	if C<> null then entitycolor(C\Cube,255,0,0)
end function



So you see, you can easily manage large numbers of objects in the lists without having to cycle through the list manually to look for something. All you need to do is refference what it is by handle() and then retrieve the list object using object()!

It is the best thing that ever happened to blitz in my opinion!


octothorpe(Posted 2005) [#13]
octo: Using Handle() and Object() is the only way you can achieve polymorphism in Blitz Basic.

frank: I would like to see an example that.


I gave you two examples in my first post. The non-trivial one was my linked list library, which is polymorphic in that it manipulates a linked list of references to arbitrary objects. The code within the linked list library doesn't need to know anything about what objects its nodes are pointing to. It can be used to manage any number of lists of any type of object.

dark: I use them and depend on them.. coding my own object management system is going to produce tons of overhead when i can merely use object/handle to control how I refference everything.

frank: The overhead is small and well worth it for control.


Frank, please try to add some value to the conversation instead of simply contradicting. You've been saying how terrible using Object/Handle is and how much better your approach is without explaining why your approach is better or what's wrong with using Object/Handle. I'm not trying to be argumentative, just constructive. :)


octothorpe(Posted 2005) [#14]
Dark Half, I'm a little confused as to why Handles are beneficial in your first example. Couldn't you simply return an object of type Gadget from CreateGadget()?
MyGadget.Gadget = creategadget(5,5)
Position(MyGadget, 15, 15)
Function CreateGadget.Gadget(X,Y)
Function Position(G.Gadget,X,Y)



Techlord(Posted 2005) [#15]
octothorpe

You have a valid point. I'm talkin trash with out producing facts. Unfortunately, I continue to fail to see how your linked list example demonstrates polymorphism. I'm truly sorry.

Wouldn't polymorphism be moreso like:
Function go_for_a_walk(this.ferret)
...
End Function

Function go_for_a_walk(this.emu)
...
End Function
The advantage of using Array of Types is speed and control inside/outside of the application.

My specific ID management system consists of adding an ID Field to the Type Structure, Array of Types, Global ID_Counter, and ID assignment method.
Type obj
	Field id%
	Field value$
End Type

Dim objID.obj(100) ;array

Global objs ;id counter

Function objNew.obj()
	this.obj = New obj
	;id assignment method
	objs=objs+1
	this\id%=objs
	objID(this\id%)=this
	;set default values
	this\value="Hello World"
	Return this
End Function

;create an object
me.obj = objNew()
myself%=me\id%
i.obj = objID(myself%)

;****************************
;a handle/object version

Type obj
	Field value$
End Type

Function objNew.obj()
	this.obj = New obj
	;set default values
	this\value="Hello World"
	Return this
End Function

;create an object
me.obj = objNew()
myself%=Handle(me)
i.obj = Object.obj(myself%)
Although my system looks more complicated, I would suspect that Handle/Object are performing similar functions behind the scenes.

Control is where the technique really shines. You can assign IDs in variety of ways. I use a FIFO stack push/pop IDs. This is good for recycling IDs with objects that are created and delete constantly. I could sort the objects by ID without interupting the structure of the collection.

External Control comes in handy as well. For example in an AI Editor I create 'Trigger {ID:1}' to activate 'Door {ID:33}'. Because I know the ID of these objects before hand its easy to tell the game engine what its suppose to do with them. Additionally, I can access the properties of the object like so:
objID(myself%)\value$="Greetings World"
Print objID(myself%)\value$

;You cannot do 
Object.obj(myself%)\value$="Greetings World"
Print Object.obj(myself%)\value$
It is true that some additional code is required. Its true that some additional memory is required, but, for the level of control its worth it IMHO.


octothorpe(Posted 2005) [#16]
Wouldn't polymorphism be moreso like:


Your example describes Function Overloading, which is a type of ad-hoc polymorphism. This is not the only kind of polymorphism. Please read the Wikipedia page on polymorphism.

Excerpt: Using parametric polymorphism, a function or datatype can be written generically so that it can deal equally well with objects of various types. For example, a function "append" that joins two lists can be constructed so that it does not depend on one particular type of list: it can append lists of integers, lists of real numbers, lists of strings, and so on.


Although my system looks more complicated, I would suspect that Handle/Object are performing similar functions behind the scenes.


Frank, you're comparing apples and orange-coloured fertilizing equipment here. Your system is a stack-managed vector container, while your "handle/object version" isn't even a container. Simply calling Handle() on something doesn't add it to a container.

Take a look at my linked list library and you'll see that it's managing the Handles in a perfectly normal linked list. I could easily write a stack-managed vector identical to your object management system (yes, with separate, assignable IDs!) which would manage Handles instead of objects - the difference would be that I wouldn't need to copy code every time I wanted a new container, and I could pass my containers to functions, and even store them in other objects. I'd also have a miniscule performance reduction for Blitz's Object/Handle overhead.

The container type you're advertising is great and everything, and I'm glad it's served you so well, but it's not ideal for all purposes; if it were, the other container classes wouldn't exist. IDs are great too, but linked lists don't use IDs. If Shifty Geezer says he wants a linked list, assume that he does.


Techlord(Posted 2005) [#17]
I was addressing the use of the undocumented functions Object/Handle and I explained my alternative system. In Blitz, Types are implemented as linked list, so there is no debate on that from my standpoint.

Ultimately the goal is to randomly access an object of Type collection without looping through all the objects. Considering the functions Object/Handle are not documented, only Mark knows what their purpose is and how they work.


octothorpe(Posted 2005) [#18]
Ultimately the goal is to randomly access an object of Type collection without looping through all the objects.


That's not the goal of a linked list.


Techlord(Posted 2005) [#19]
octothorpe

You win.


DH(Posted 2005) [#20]
Dark Half, I'm a little confused as to why Handles are beneficial in your first example. Couldn't you simply return an object of type Gadget from CreateGadget()?


Because if I return the handle, then the user has to know what type to assign his variable before he can call my functions!

Simply returning a handle and referencing it with object keeps my interworkings transparent to the user, so the user doesn't have to care how the system works as long as it works.

Just the way I like to do things I guess.