Blitz2D/3D/+ like type access...
BlitzMax Forums/BlitzMax Tutorials/Blitz2D/3D/+ like type access...
| ||
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... |
| ||
Very helpful :) |
| ||
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)? |
| ||
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. |
| ||
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 |
| ||
Jolly good show. Will be using all. |
| ||
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!" |
| ||
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. :-) |
| ||
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. |
| ||
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. |
| ||
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? |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
Sorry, you are right, I was just nitpicking. :) |
| ||
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 |
| ||
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. |
| ||
You should use tList.addfirst(), not tList.addlast(): http://blitzbasic.com/Community/posts.php?topic=59747 |
| ||
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) |