Tutorial - Types in Blitz3D

Blitz3D Forums/Blitz3D Tutorials/Tutorial - Types in Blitz3D

markcw(Posted 2008) [#1]
Note: this tutorial was written for Blitz3D but the theory and code is exactly the same for BlitzPlus apart from example 6 which relates to entities (and the Print command which writes to the console).

Types are known as objects because they can hold a collection of variables. This feature of the language allows object-based programming which is a limited form of object-oriented programming. Types are stored at runtime in a linked list which is accessed every time you use one of the type-related commands ie. After, Before, Delete, Each, First, Handle, Insert, Last, New, Null, Object.

To create a type the first thing you need is a type structure with some fields in it. Next you create an instance of the type, an instance is a pointer or reference to a type object. Finally you can set and get type fields using the "pointer\field" syntax.
;Example 1: How to create a type

Type MYTYPE
 Field myinteger,myfloat#,mystring$
 Field myintegerarray[2],myfloatarray#[2],mystringarray$[2]
End Type

SeedRnd(MilliSecs())

pType.MYTYPE=New MYTYPE

pType\myinteger=101
pType\myfloat#=1.23
pType\mystring$="foo"
pType\myintegerarray[0]=Rand(-10,10)
pType\myfloatarray#[1]=Rnd(-10.5,10.5)
pType\mystringarray$[2]="bar"

Print "myinteger="+pType\myinteger
Print "myfloat#="+pType\myfloat#
Print "mystring$="+pType\mystring$
Print "myintegerarray[0]="+pType\myintegerarray[0]
Print "myfloatarray#[1]="+pType\myfloatarray#[1]
Print "mystringarray$[2]="+pType\mystringarray$[2]

WaitKey()
End

Type pointers themselves can't be treated like a normal variable, for example if you do this the compiler will throw an error.
Print pType

This is because a type pointer stores an address which can't be accessed directly, but what if you want to store a bunch of types in an array? No problem, arrays can be assigned a type or you can use the Handle command.
Print Handle(pType)

Handle takes a type pointer and returns a runtime index number which represents the pointer. An important thing to remember is that a pointer won't be indexed until you pass it to Handle. To be precise, the counter starts at zero and increments every time you pass it a new pointer. When you delete a type its index is reset and that number isn't used again.
;Example 2: How Handle and type arrays work

Type MYTYPE
 Field myinteger,myfloat#,mystring$
End Type

Dim pArray.MYTYPE(4)

pArray(3)=New MYTYPE
pArray(2)=New MYTYPE
pArray(1)=New MYTYPE

Print "pArray(1)="+Handle(pArray(1))
Print "pArray(2)="+Handle(pArray(2))
Print "pArray(3)="+Handle(pArray(3))

Delete pArray(2)
pArray(4)=New MYTYPE

For id=1 To 4
 Print "pArray("+id+")="+Handle(pArray(id))
Next

For pTemp.MYTYPE=Each MYTYPE
 Print "pTemp="+Handle(pTemp)
Next

For id=1 To 4
 pTemp.MYTYPE=pArray(id)
 Print "pTemp="+Handle(pTemp)+" pArray("+id+")="+Handle(pArray(id))
Next

WaitKey()
End

The Object command is the reverse of Handle, it takes a type handle and returns the pointer to it. If the handle doesn't exist it will return Null. Using a null pointer will throw a runtime error so you may need to check it exists before you access it. If you store type handles in an integer array you can then access the pointers using Object. Handle and Object are useful because they give us more ways to access types.
;Example 3: How Object and handle arrays work

Type MYTYPE
 Field myinteger,myfloat#,mystring$
End Type

Dim myHandle(4)

For id=1 To 3
 pTemp.MYTYPE=New MYTYPE
 myHandle(id)=Handle(pTemp)
 Print "myHandle("+id+")="+myHandle(id)
Next

Delete Object.MYTYPE(myHandle(2))
pTemp.MYTYPE=New MYTYPE
myHandle(4)=Handle(pTemp)

For pTemp.MYTYPE=Each MYTYPE
 Print "pTemp="+Handle(pTemp)
Next

For id=1 To 4
 pTemp.MYTYPE=Object.MYTYPE(myHandle(id))
 Print "pTemp="+Handle(pTemp)+" myHandle("+id+")="+myHandle(id)
Next

WaitKey()
End

When you delete a type its place in the type list is deleted and all the objects after it are moved up the list. So if you're only using a For Each loop to access your types you won't need to check for null pointers, but other methods will usually need a null pointer check. Another way to access types is with the First, Last, After and Before commands. First returns the first pointer in a type list, similarly Last returns the last one, After returns the pointer after a specified pointer and likewise Before returns the one before it. Insert moves a pointer from one place to another in a type list, so it's one way to sort a list, another way would be to just sort handles in an array.
;Example 4: How First, Last, After, Before and Insert work

Type MYTYPE
 Field myinteger,myfloat#,mystring$
End Type

For id=1 To 5
 pTemp.MYTYPE=New MYTYPE
Next

Delete First MYTYPE

pTemp.MYTYPE=First MYTYPE
For id=1 To 5
 If pTemp<>Null
  Print "First/After Loop="+Handle(pTemp)
  pTemp=After pTemp
 EndIf
Next

pTemp.MYTYPE=Last MYTYPE
For id=1 To 5
 If pTemp<>Null
  Print "Last/Before Loop="+Handle(pTemp)
  pTemp=Before pTemp
 EndIf
Next

Insert Last MYTYPE After First MYTYPE
Insert Last MYTYPE Before First MYTYPE

For pTemp.MYTYPE=Each MYTYPE
 Print "Shuffled List="+Handle(pTemp)
Next

For pOuter.MYTYPE=Each MYTYPE
 For pInner.MYTYPE=Each MYTYPE
  pTemp.MYTYPE=After pInner
  If Handle(pInner)>Handle(pTemp) And pTemp<>Null
   Insert pInner After pTemp
  EndIf
 Next
Next

For pTemp.MYTYPE=Each MYTYPE
 Print "Sorted by Handle="+Handle(pTemp)
Next

WaitKey()
End

Functions also accept types as parameters and return values. This gives us a workaround with arrays, we can't pass arrays as parameters but we can pass a static array stored in a type. Another feature of types is that you can nest types in other types which is a basic form of inheritance. Nested types will not exist when you create their parent object, so they need to be created after the parent. Also, like variables we can use the Local command when creating types in functions to avoid a conflict with a global of the same name. An alternative to passing and returning types is to use Object and Handle. The advantage of this is more flexibility, for example you can pass all types in one parameter using Handle which can be used to create a form of polymorphism.

Note: inheritance is the ability to form new types of objects using objects that have already been defined. Polymorphism is the ability to handle different data types.
;Example 5: How types in functions work

Type MYTYPE
 Field myint,myptr.MYNESTED
End Type

Type MYNESTED
 Field myfloat#
End Type

Dim myHandle(4)

For id=1 To 4
 pTemp.MYTYPE=TypeContructor()
Next

For pTemp.MYTYPE=Each MYTYPE
 Print "pTemp="+Handle(pTemp)+" myptr="+Handle(pTemp\myptr)
Next

pTemp.MYTYPE=First MYTYPE
TypeUpdate(pTemp,101)
Print "myint="+pTemp\myint

For pTemp.MYTYPE=Each MYTYPE
 TypeDestructor(pTemp)
Next

For id=1 To 4
 myHandle(id)=HandleContructor()
Next

For pTemp.MYTYPE=Each MYTYPE
 Print "pTemp="+Handle(pTemp)+" myptr="+Handle(pTemp\myptr)
Next

HandleUpdate(myHandle(1),1.23)
pTemp.MYTYPE=Object.MYTYPE(myHandle(1))
Print "myptr\myfloat#="+pTemp\myptr\myfloat#

For id=1 To 4
 HandleDestructor(myHandle(id))
Next

WaitKey()
End

Function TypeContructor.MYTYPE(myparameter=0)

 Local pType.MYTYPE=New MYTYPE
 pType\myptr=New MYNESTED
 pType\myint=myparameter
 Return pType

End Function

Function HandleContructor(myparameter=0)

 Local pType.MYTYPE=New MYTYPE
 pType\myptr=New MYNESTED
 pType\myint=myparameter
 Return Handle(pType)

End Function

Function TypeDestructor(pType.MYTYPE)

 Delete pType\myptr
 Delete pType

End Function

Function HandleDestructor(myHandle=0)

 Local pType.MYTYPE=Object.MYTYPE(myHandle)
 Delete pType\myptr
 Delete pType

End Function

Function TypeUpdate(pType.MYTYPE,myparameter=0)

 If pType=Null Then Return False
 pType\myint=pType\myint+myparameter
 Return True

End Function

Function HandleUpdate(myHandle=0,myparameter#=0)

 Local pType.MYTYPE=Object.MYTYPE(myHandle)
 If pType<>Null
  pType\myptr\myfloat#=myparameter#
 EndIf

End Function

In Blitz3D, there is a well-known trick to accessing types from an entity. It relies on the fact that entities have names which are never used so we can store a type handle there without any problems. This lets us store as much extra information about entities as we want.
;Example 6: How to associate a type with an entity

Type ENTITY
 Field x#,y#,z#
End Type

Graphics3D 320,240,0,2
SetBuffer BackBuffer()

camera=CreateCamera()
light=CreateLight()

pTemp.ENTITY=New ENTITY
pTemp\x#=6.4
pTemp\y#=6.4
entity1=CreateMesh()
NameEntity entity1,Handle(pTemp)
Print "entity1="+EntityName(entity1)

pTemp.ENTITY=New ENTITY
pTemp\x#=12.8
pTemp\y#=12.8
entity2=CreateMesh()
NameEntity entity2,Handle(pTemp)
Print "entity2="+EntityName(entity2)

pTemp.ENTITY=Object.ENTITY(EntityName(entity1))
Print "pTemp\x="+pTemp\x#+" pTemp\y="+pTemp\y#
pTemp.ENTITY=Object.ENTITY(EntityName(entity2))
Print "pTemp\x="+pTemp\x#+" pTemp\y="+pTemp\y#

WaitKey()
End

Images aren't entities so what if you want to store extra information about images? You can create functions to simulate NameEntity and EntityName. You could use arrays but they are not dynamic, fortunately banks are so they are the best choice. Banks can be resized without loss of data so we can have as many entries as we need.

In the following example, the first integer in the bank is reserved for counting the number of existing entries and then images are associated with a type by storing them as a double entry. The SetImageType function creates a new entry or changes the type handle if the entry already exists. GetImageType returns the type handle for an image. DeleteImageType removes an entry and then all the entries after it are moved up.
;Example 7: How to associate a type with an image using a bank

Global bImageType=CreateBank(4)

Type IMAGE
 Field x,y,w,h
End Type

Graphics 320,240,0,2
SetBuffer BackBuffer()

pTemp.IMAGE=New IMAGE
pTemp\w=64
pTemp\h=64
image1=CreateImage(pTemp\w,pTemp\h)
SetImageType(image1,Handle(pTemp))
Print "image1="+GetImageType(image1)

pTemp.IMAGE=New IMAGE
pTemp\w=128
pTemp\h=128
image2=CreateImage(pTemp\w,pTemp\h)
SetImageType(image2,Handle(pTemp))
Print "image2="+GetImageType(image2)

pTemp.IMAGE=Object.IMAGE(GetImageType(image1))
Print "pTemp\w="+pTemp\w+" pTemp\h="+pTemp\h
pTemp.IMAGE=Object.IMAGE(GetImageType(image2))
Print "pTemp\w="+pTemp\w+" pTemp\h="+pTemp\h

Print "BankSize="+BankSize(bImageType)
Print PeekInt(bImageType,0)
For id=4 To BankSize(bImageType)-4 Step 8
 Print PeekInt(bImageType,id)+" "+PeekInt(bImageType,id+4)
Next

Delete Object.IMAGE(GetImageType(image1))
DeleteImageType(image1)
FreeImage image1

Print "BankSize="+BankSize(bImageType)
Print PeekInt(bImageType,0)
For id=4 To BankSize(bImageType)-4 Step 8
 Print PeekInt(bImageType,id)+" "+PeekInt(bImageType,id+4)
Next

WaitKey()
End

Function SetImageType(image=0,typeid=0)

 Local id,found
 If image=0 Then Return False
 For id=4 To BankSize(bImageType)-4 Step 8
  If image=PeekInt(bImageType,id)
   found=True
   Exit
  EndIf
 Next
 If found=True
  id=PeekInt(bImageType,0)
  PokeInt bImageType,id*8,typeid
 Else
  id=PeekInt(bImageType,0)+1
  ResizeBank bImageType,4+(id*8)
  PokeInt bImageType,0,id
  PokeInt bImageType,(id*8)-4,image
  PokeInt bImageType,id*8,typeid
 EndIf

End Function

Function GetImageType(image=0)

 Local id,found
 If image=0 Then Return False
 For id=4 To BankSize(bImageType)-4 Step 8
  If image=PeekInt(bImageType,id)
   found=True
   Return PeekInt(bImageType,id+4)
  EndIf
 Next
 If found=False Then RuntimeError "Image entry not found"

End Function

Function DeleteImageType(image=0)

 Local id,at
 If image=0 Then Return False
 For id=4 To BankSize(bImageType)-4 Step 8
  If image=PeekInt(bImageType,id)
   at=id+8
   Exit
  EndIf
 Next
 If at>0
  CopyBank bImageType,at,bImageType,at-8,BankSize(bImageType)-at
  id=PeekInt(bImageType,0)-1
  ResizeBank bImageType,4+(id*8)
  PokeInt bImageType,0,id
 EndIf

End Function

That's it! I have tried to cover all the main ways you can use types. If you think I missed something important then let me know so I can add it to this tutorial.


ervin(Posted 2008) [#2]
That's tremendous.
A lot of people will benefit from that.


Fernhout(Posted 2008) [#3]
Hey thanx for the explenation. Is was searching for it. Nice work. Its help me a lot.


Pirate(Posted 2008) [#4]
thanks markcw,
this tutorial has really helped me with types and was presented very well...which means that i could actually understand it....thanks


WolRon(Posted 2012) [#5]
Here's a link to a page that tries more to describe what a Type is (and how you can basically use it) if in case the above tutorial was a bit overwhelming to the reader:
Blitz Types (taken from the BASIC Prog. Tut. in my sig)


Ross C(Posted 2012) [#6]
Very nice way of linking images to types. That's always caused me hassle...


yaragad(Posted 2014) [#7]
Nice! I have two questions about it:

1. I'm creating about 100 types and each type will store about 1000 objects (like a database). What is the fastest way to implement Types that are related? For example: I have Type City and it has an idCountry, so I have to access to the Country in the Type Country as fast as possible.

2. I've seen that the examples create an Array of Objects within the Type (i.e. Dim pArray.MYTYPE(4)). What about dinamic sized array types?

Thanks!