Tutorial - Types in Blitz3D
Blitz3D Forums/Blitz3D Tutorials/Tutorial - Types in Blitz3D
| ||
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. |
| ||
That's tremendous. A lot of people will benefit from that. |
| ||
Hey thanx for the explenation. Is was searching for it. Nice work. Its help me a lot. |
| ||
thanks markcw, this tutorial has really helped me with types and was presented very well...which means that i could actually understand it....thanks |
| ||
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) |
| ||
Very nice way of linking images to types. That's always caused me hassle... |
| ||
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! |