Blitz2D/3D/+ like type access...

BlitzMax Forums/BlitzMax Tutorials/Blitz2D/3D/+ like type access...

fredborg(Posted 2005) [#1]
Hi,

A lot of people coming from previous versions of Blitz seem to have problems grasping the concept of how BlitzMax handles types. The good news is that you can emulate oldschool Blitz behavior... It doesn't make it any simpler, but this is one way of doing it:
Type mytype
	Field _value:Int	= 0

	'
	' Global list which holds all instances
	Global _list:TList = New TList

	'
	' This adds a new instance to the global list
	Method New()
		_list.addlast(Self)
	EndMethod

	'
	' Inserts this instance after the 'other' instance
	Method InsertAfter(other:mytype)
		_list.remove(Self)
		_list.InsertAfterLink(Self,_list.FindLink(other))
	EndMethod

	'
	' Inserts this instance before the 'other' instance
	Method InsertBefore(other:mytype)
		_list.remove(Self)
		_list.InsertBeforeLink(Self,_list.FindLink(other))
	EndMethod
	
	'
	' Gets the next instance in the global list
	Method After:mytype()
		Local link:TLink = _list.FindLink(Self).NextLink()
		If link
			Return mytype(link.Value())
		Else
			Return Null
		EndIf
	EndMethod

	'
	' Gets the previous instance in the global list
	Method Before:mytype()
		Local link:TLink = _list.FindLink(Self).PrevLink()
		If link
			Return mytype(link.Value())
		Else
			Return Null
		EndIf
	EndMethod
	
	'
	' Removes this instance from the list
	Method Destroy()
		_list.remove(Self)
	EndMethod
	
	'
	' Removes all instances
	Function DestroyEach()
		_list.clear()
	EndFunction
	
	'
	' Gets the last instance in the list
	Function Last:mytype()
		Return mytype(_list.last())
	EndFunction

	'
	' Gets the first instance in the list
	Function First:mytype()
		Return mytype(_list.first())
	EndFunction
		
End Type

'
' Lets create a bunch of dummy type instances
For i = 0 To 5
	m:mytype = New mytype
	m._value = i
Next

'
' For..EachIn
Print "Going through each instance, using a For..EachIn loop"
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

'
' InsertBefore
Print "Inserting new instance before first instance"
m:mytype = New mytype
m._value = 6
m.InsertBefore mytype.First()
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

'
' InsertAfter
Print "Inserting new instance after first instance"
m:mytype = New mytype
m._value = 7
m.InsertAfter mytype.First()
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

'
' While..Wend (forward)
Print "Going through each instance, using a While..Wend loop"
m:mytype = mytype.first()
While m
	Print m._value
	m = m.after()
Wend
Print "---~n"

'
' While..Wend (backward)
Print "Going Backwards through each instance, using a While..Wend loop"
m:mytype = mytype.last()
While m
	Print m._value
	m = m.before()
Wend
Print "---~n"

'
' Destroy
Print "Removing the first instance, and showing remaining instances"
m:mytype = mytype.first()
m.Destroy()
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

'
' DestroyEach
Print "Removing each instance, and showing whats left"
mytype.DestroyEach()
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

Print "That's all folks!"
As you can see the basic principle is to have a global list inside a type. And then when adding a new type instance (using bla:type = New type), it is automatically added to the type list.

As you will also notice is that to emulate all the oldschool Blitz commands you need quite a few methods and functions inside your type. The good part is that you will need to change very little to get it working on a new type of type.

One function/method is missing from this, namely the infamous 'Insert bla1 before/after bla2', but if you examine the code I'm sure you can add that yourself.

As mentioned at the beginning, it doesn't make it any easier to use a type, but typically you will only need a 'New()' and 'Destroy()' method...

Did that make any sense?

[Edit] Added InsertBefore + InsertAfter and tuned a bit...


Robert Cummings(Posted 2005) [#2]
Very helpful :)


Who was John Galt?(Posted 2005) [#3]
Interesting...
Didn't know you could overload 'New' like that. I take it Max creates a New type instance before calling your new() method on it (you don't explicitly create a new instance as far as I can see)?


fredborg(Posted 2005) [#4]
Hi,

Yep, the new type instance is created first and then the type specific New() method is called afterwards. It's an excellent implementation which allows you to do all sorts of weird initialization of type instances.


EOF(Posted 2005) [#5]
InsertBefore and InsertAfter functions which can be adapted for the above:
Function InsertAfter(list:TList,a:Object,b:Object)
	list.InsertAfterLink(b,list.FindLink(a))
End Function

Function InsertBefore(list:TList,a:Object,b:Object)
	list.InsertBeforeLink(b,list.FindLink(a))
End Function


' example

Local t:TList=New TList
t.AddLast "Hello"
t.AddLast "world"

InsertAfter t,"Hello","my"
InsertBefore t,"world","magic"

For Local a$=EachIn t
	Print a$
Next



Robert Cummings(Posted 2005) [#6]
Jolly good show. Will be using all.


Beaker(Posted 2005) [#7]
Fredborgs example can be simplified a little to make it more transportable to other Types:
'Old style Blitz lists by fredborg
'beak tweak

Type mytype
	Field _value:Int	= 0

	'
	' Global list which holds all instances
	Global _list:TList

	'
	' This adds a new instance to the global list
	Method New()
		If _list = Null
			_list = New TList
		EndIf
		_list.addlast(Self)
	EndMethod
	
	'
	' Gets the next instance in the global list
	Method After:mytype()
		Local link:TLink = ListFindLink(_list,Self).NextLink()
		If link
			Return mytype(link.Value())
		Else
			Return Null
		EndIf
	EndMethod

	'
	' Gets the previous instance in the global list
	Method Before:mytype()
		Local link:TLink = ListFindLink(_list,Self).PrevLink()
		If link
			Return mytype(link.Value())
		Else
			Return Null
		EndIf
	EndMethod
	
	'
	' Removes this instance from the list
	Method Destroy()
		_list.remove(Self)
	EndMethod
	
	'
	' Removes all instances
	Function DestroyEach()
		_list.clear()
	EndFunction
	
	'
	' Gets the last instance in the list
	Function Last:mytype()
		Return mytype(_list.last())
	EndFunction

	'
	' Gets the first instance in the list
	Function First:mytype()
		Return mytype(_list.first())
	EndFunction
		
End Type

'
' Lets create a bunch of dummy type instances
For i = 0 To 5
	m:mytype = New mytype
	m._value = i
Next

'
' For..EachIn
Print "Going through each instance, using a For..EachIn loop"
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

'
' While..Wend (forward)
Print "Going through each instance, using a While..Wend loop"
m:mytype = mytype.first()
While m
	Print m._value
	m = m.after()
Wend
Print "---~n"

'
' While..Wend (backward)
Print "Going Backwards through each instance, using a While..Wend loop"
m:mytype = mytype.last()
While m
	Print m._value
	m = m.before()
Wend
Print "---~n"

'
' Destroy
Print "Removing the first instance, and showing remaining instances"
m:mytype = mytype.first()
m.Destroy()
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

'
' DestroyEach
Print "Removing each instance, and showing whats left"
mytype.DestroyEach()
For m:mytype = EachIn mytype._list
	Print m._value
Next
Print "---~n"

Print "That's all folks!"



sswift(Posted 2005) [#8]
Am I to understand that types in Blitzmax are now merely ordinary C style structures, no longer automatically treated as memberes of a set of linked lists?

Also, what the hell is the difference between a method and a function? They look like they're used exaclty the same as one another.

And what's with the _ in front of all the type fields? Unless that does something in BlitzMax, I say it's a bad programming convention because you're not supposed to start variable names with anything other than a letter, and it makes me think it does something, like when you put a * in front of something in C. :-)


Koriolis(Posted 2005) [#9]
Am I to understand that types in Blitzmax are now merely ordinary C style structures
I'm not sure to see what you call "C style structures". Custom types are like what is called classes in most other languages.
no longer automatically treated as memberes of a set of linked lists
Correct. This is really fine like that, more consistent. You can easily have any number of object lists you want, and BlitzMax comes with built-in list support, so that's really easy.

Also, what the hell is the difference between a method and a function?
The same as in other OO languages. A method is always applied to given object, you can see a method call as a function call with an implicit ("hidden") parameter, which is the object on which the method call is applied.
But unlike a function with an object parameter, you can *override* a method. There is no way I can easily explain in a few lines what it really means, I suggest you read some OO tutorial (I seem to recall that Mark done such a tutorial, which is a good starting point and should be more than enough to start with).

And what's with the _ in front of all the type fields? Unless that does something in BlitzMax, I say it's a bad programming convention because you're not supposed to start variable names with anything other than a letter
Who said that? This would be true in C, but it's BlitzMax. The leading underscore is only a naming convention, to easily see what is a field and what is not.
I personnaly don't like this convention, and don't use it. I prefer putting a *trailing* underscore on fields that should be treated as private (not accessed from the outside of the type), and only these ones.
I use a trailing underscore rather than a leading one, for 2 reasons:
- the leading underscore makes it hard to read, which is really not good. We read from left to right, and I usually don't need that little bit of hint, so better not have it get in the way. A trailing underscore on the other hand is far less annoying to me.
- C and C++ allow leading underscores only in very few cases (and then you'd better not use them at all). As I program in C++ every day, better not confuse myself and use something that is a no no in C++. Not to say what I program is 95% C++ code, exposed to BlitzMax via a DLL.

I really find this convention more sensible.


Beaker(Posted 2005) [#10]
You are correct: Type objects aren't automatically stored in a list in BlitzMax.

Difference between a method and a function: a method is applied to a particular object, a function isn't. Like so:
myThing.doMethodThing() ' calls doMethodThing which changes myThing
myType.doFunctionThing() ' calls doFunctionThing but has no specific object to work with


Almost like a function is a global call and a method is a local call. Very handy.

You don't need an underscore. I think fred probably used it so that it wouldn't clash with any other fields/globals/funcs etc.


sswift(Posted 2005) [#11]
I still don't understand why you need both.

If you only could have methods in a type, is there anything you couldn't do that you could do if you also had functions?


Koriolis(Posted 2005) [#12]
Well it's like saying that given that arrays can contain any type of data, why not have everything be an array, and just make one-lenghted arrays if you need only one variable. Similarly, you can do with a method everything you can with a function, but the reverse is not true and when you only need a function, better just create a function.

If you write a dummy "Print" function, you probably don't need any object for that. Then use a function, not a method. Functions inside types are no different. Functions inside types work just like function outside types, you are just given the ability to put them inside the type, which provides a separate namespace (MyType1.Print and MyType2.Print won't collide). So by exampleif you have a simple function that you plan to use from inside methods of the types (or in the context of using objects of these types), then naturally you'll put them inside the type.

I guess this "namespace" issue answers your question.


Beaker(Posted 2005) [#13]
Its all just part of the OO "big picture".

In some ways I disagree with Koriolis, in my eyes they are both very different. A function can't be called on a specific object, and a method can't be called on anything but an object.

They both prevent you from using them in the wrong way, for example: you won't ever be able to call a method unless you have an object for it to manipulate.

Similarly, a function can't be called unless you refer to its Type name (or namespace), which lets you have many functions named Hello() but each one is only associated with one Type:

Type a
	Field x,y
	
	Function Hello()
		Print "A says Hello"
	End Function
EndType

Type b
	Field q,p
	
	Function Hello()
		Print "B says Hello"
	End Function
EndType

a.Hello
b.Hello



Koriolis(Posted 2005) [#14]
Even after rereading my post I hardly see how you could believe I was saying a function and a method are the same to me. I just said that you could technically use only methods as SSwift suggests, but I also implied it wouldn't make much sense.
I know I'm not often crystal clear, but here you'll make me paranoid about the crazy difference between what I say and what people understand.

An additional note: yes, function and methods are actually far closer than what you imply. Saying that a method can't be called on anything other than an object is true, but not enough. Because similarly a function taking an object parameter can't be called with a parameter that would be anything other than an (compatible) object. That's really the same thing here.

The real *BIG* difference is the ability to override a method, which is pretty much what OO is all about. And this ability is what makes the object you apply the method to such a particular (hidden and implcit) parameter. Here lies the very important difference to me.
Of course, that's the technical side of thing. From an OO programmer's view point it's healthier to really think in terms of objects, and "feel" methods as an operation that "belongs" to the type.


Beaker(Posted 2005) [#15]
Sorry, you are right, I was just nitpicking. :)


Duckstab[o](Posted 2005) [#16]
Type Functions are related to the Type itself

Type Methods are related to the instance of the type

Global Playerlist:TList=CreateList() 'To Store all the players


Type Player
	
	Field Name$
	Field HP,MP
	
	Function Create(_Name$,_HP,_MP)
		Temp:Player=New Player
			Temp.Name$=_Name$
			Temp.HP=_HP
			Temp.MP=_MP
		ListAddLast playerlist,temp	
	End Function
	
End Type



Player.create("Bobby",100,40) 'creates a new player using the function create in the type and adds it to the playerlist


Now lets add some new functions to to the type




Global Playerlist:TList=CreateList() 'To Store all the players

Type Player
	
	Field Name$
	Field HP,MP
	
	Function Create(_Name$,_HP,_MP)
		Temp:Player=New Player
			Temp.Name$=_Name$
			Temp.HP=_HP
			Temp.MP=_MP
		ListAddLast playerlist,temp	
	End Function
	
	Function PrintList()
		For Temp:Player=EachIn Playerlist
			Print
			Print "NAME= "+temp.Name$
			Print "HP  = "+temp.HP
			Print "MP  = "+temp.MP
			Print
		Next
	End Function
	
	
End Type

Player.create("Bobby",100,40) 'creates a new player using the function create in the type 
Player.create("Bilby",400,40)

Player.Printlist' list all players in playerlist


now using a combination of functions and type

Functions to create and find
Methods to alter an instance

Global Playerlist:TList=CreateList() 'To Store all the players


Type Player
	
	Field Name$
	Field HP,MP
	
	Function Create(_Name$,_HP,_MP)
		Temp:Player=New Player
			Temp.Name$=_Name$
			Temp.HP=_HP
			Temp.MP=_MP
		ListAddLast playerlist,temp	
	End Function
	
	Function PrintList()
		For Temp:Player=EachIn Playerlist
			Print
			Print "NAME= "+temp.Name$
			Print "HP  = "+temp.HP
			Print "MP  = "+temp.MP
			Print
		Next
	End Function
	
	Function Find:Player(_Name$)
		For Local P:player = EachIn playerList
			If P.Name$ = _Name$ Then Return P
		Next
		Return Null
	End Function

	
	Method Add_HP(_Boost)
	  Hp:+_Boost
	End Method
	
	Method Add_MP(_Boost)
	  Mp:+_Boost
	End Method
	
End Type



Player.create("Bobby",100,40) 'creates a new player using the function create in the type 
Player.create("Bilby",400,40)

Player.Printlist' list all players in playerlist

player.Find("Bobby").Add_Hp(40) 'this find an instance using a type function and applys the method add_hp
player.Find("Bilby").Add_Hp(140)

Player.Printlist' list all players in playerlist

player.Find("Bobby").Add_Mp(40)
player.Find("Bilby").Add_Mp(140)

Player.Printlist' list all players in playerlist


The real gain in using method and functions is seen when you have several types and instances as using these kind programming styles will cut down the amount of coding and makes finding and altering instances faster and more reliable



Global Playerlist:TList=CreateList() 'To Store all the players
Global Baddielist:TList=CreateList() 'To store all the baddies



Type Player
	
	Field Name$
	Field HP,MP
	
	Function Create(_Name$,_HP,_MP)
		Temp:Player=New Player
			Temp.Name$=_Name$
			Temp.HP=_HP
			Temp.MP=_MP
		ListAddLast playerlist,temp	
	End Function
	
	Function PrintList()
		For Temp:Player=EachIn Playerlist
			Print
			Print "NAME= "+temp.Name$
			Print "HP  = "+temp.HP
			Print "MP  = "+temp.MP
			Print
		Next
	End Function
	
	Function Find:Player(_Name$)
		For Local P:player = EachIn playerList
			If P.Name$ = _Name$ Then Return P
		Next
		Return Null
	End Function

	
	Method Add_HP(_Boost)
	  Hp:+_Boost
	End Method
	
	Method Add_MP(_Boost)
	  Mp:+_Boost
	End Method
	
End Type

Type Baddie
	
	Field Name$
	Field HP,MP
	
	Function Create(_Name$,_HP,_MP)
		Temp:Baddie=New Baddie
			Temp.Name$=_Name$
			Temp.HP=_HP
			Temp.MP=_MP
		ListAddLast Baddielist,temp	
	End Function
	
	Function PrintList()
		For Temp:Baddie=EachIn Baddielist
			Print
			Print "NAME= "+temp.Name$
			Print "HP  = "+temp.HP
			Print "MP  = "+temp.MP
			Print
		Next
	End Function
	
	Function Find:Baddie(_Name$)
		For Local B:Baddie = EachIn BaddieList
			If B.Name$ = _Name$ Then Return B
		Next
		Return Null
	End Function

	
	Method Add_HP(_Boost)
	  Hp:+_Boost
	End Method
	
	Method Add_MP(_Boost)
	  Mp:+_Boost
	End Method
	
End Type



Player.create("Bobby",100,40) 'creates a new player using the function create in the type 
Player.create("Bilby",400,40)

Player.Printlist' list all players in playerlist

player.Find("Bobby").Add_Hp(40)
player.Find("Bilby").Add_Hp(140)

Player.Printlist' list all players in playerlist

player.Find("Bobby").Add_Mp(40)
player.Find("Bilby").Add_Mp(140)

Player.Printlist' list all players in playerlist

baddie.create("Orc",300,20)
baddie.create("Goblin",200,10)

baddie.printlist' list all baddies in baddielist

baddie.find("Orc").Add_Hp(400)
baddie.find("Goblin").Add_Mp(50)

baddie.printlist' list all baddies in baddielist




AD(Posted 2006) [#17]
Thank you Duckstab[o], your tutorial really helped me understand how Blitz Max uses types -- I like the way its changed, although it can be quite confused at first.

For a long time I've been working with the Blitz Basic version I got free with a magazine and I finally decided to upgrade because of the GUI stuff, but mainly so I could learn and do OO stuff.

Many thanks again.


JoshK(Posted 2006) [#18]
You should use tList.addfirst(), not tList.addlast():
http://blitzbasic.com/Community/posts.php?topic=59747


grable(Posted 2008) [#19]
You should use tList.addfirst(), not tList.addlast()

Why? They both take the same amount of time to complete. There is no reason to suggest using one over the other unless there is some specific use case for it.
(like you specify in your link)