Passing more than one Type to a Function
BlitzMax Forums/BlitzMax Programming/Passing more than one Type to a Function
| ||
Hello! =) Is it possible to pass more than one Type to a Function parameter? As a example, instead of this, doesnothing (buyer) Function doesnothing (g:generic Var) Print "passed type with value of "+ g.yawn End Function something like this maybe? (this is pseudocode) doesnothing (buyer:generic) doesnothing (notbuyer:namebrand) Function doesnothing (g Var) Print "passed type with value of "+ g.yawn End Function Is there a way to do something like this? If not what is the best workaround? Thanks. |
| ||
Whats wrong with using Object? Either that or some common base class. |
| ||
Okay, I did this,Function doesnothing (g:Object Var) Print "passed type with value of "+ g.yawn End Function But then it complains about the identifier for the field variable not being found. I've tried calling the Function like this, doesnothing (buyer:generic) and like this, doesnothing (buyer) Both do the same thing. I didn't really know anything about Objects before you bought it up. So did I miss something? |
| ||
The "param-type" defines what the function accepts. Within the function it is up to you think it is something else. Why should you do this? If you have a function "doIt(g:TMyType)" then you cannot compile your code until the passed param is of type TMyType (or extended from it). So the caller needs to know at least the param type (also important when having code in individual files - not importing each other). So "object" is the generic approach to solve it. the "least common determinator" for all objects in a game (string, int, byte ... are NOT "object"-based). Ok, so in your function you might do a: local = TMyType(g) if not myType then Throw "passed invalid type" print "yawn:" +myType.yawn Again: this allows the function-caller to not need to know what the function expects (the function could check for multiple types... eg. "TCar" or "TBicycle"). This gets even more obvious if you have two functions - and within the first you call the second with the param given to the first one. The first one does not need to know about the type itself - it just passes it to the next function. bye Ron |
| ||
Derron sums it up nicely. Though String is also an Object just to clarify, and its immutable. And i notice your using Var, but never assign to it. In which case its not needed, and is slower than without. In case you didnt know, Var passes the address of an existing variable outside the function and this allows one to change it. |
| ||
Okay, Derron I tried using the code you gave like this,Function doesnothing (g:Object) Local myType = Object(g) If Not myType Then Throw "passed invalid type" Print "yawn:" +myType.yawn End Function I have no idea if thats how I was supposed to use that, but it is still giving an error that says (identifier 'yawn' not found). |
| ||
Close but not quite, you need to cast to a known object. Base Object is in effect an empty object.Type Type1 Field A:Int = 1 EndType Type Type2 Field B:Int = 2 EndType Function doesnothing (g:Object) Local t1:Type1 = Type1(g) If t1 Then Print "Type1 = " + t1.A Else Local t2:Type2 = Type2(g) If t2 Then Print "Type2 = " + t2.B EndIf EndIf End Function doesnothing( New Type1) doesnothing( New Type2) |
| ||
Yes! My test code is working now. This is what I have,doesnothing (buyer) doesnothing (notbuyer) Function doesnothing (g:Object) Local myType:generic = generic(g) Print "yawn:" +myType.yawn End Function So if I understand this correctly, the first line in the Function transforms the passed type into a local one to use. And I have a few follow up questions. What did this line of code do? I know that it is some kind of error check, but I don't know what it is checking. Do I need it? If Not myType Then Throw "passed invalid type" What happens if I use a field in the Function that the original type didn't have? And is there anything I need to watch out for with this? Thanks grable and Derron! =) |
| ||
Local myType:generic = generic(g) If Not myType Then Throw "passed invalid type" The first line "Cast" the object to a "generic" object (seriously, this example is really confusing ^^) The second line "assert" the object exists -> because you could call by error the function using any objects that are not "generic" instances Have a seen on this, it might clarified what object casting is. Type T1 End Type Type T2 End Type Local o:Object = New T1 Print "o is an instance of T1 :"+( T1(o) <> Null ) Print "o is an instance of T2 :"+( T2(o) <> Null ) |
| ||
So if I understand this correctly, the first line in the Function transforms the passed type into a local one to use. Essentially, yes. Though its not transforming, but casting. Which is it checks if the object really is of that type, and if so it returns that object as that type. Its the same object just looked at through different glasses. What did this line of code do? I know that it is some kind of error check, but I don't know what it is checking. Do I need it? Yes its an error check, but its not really an error if a cast does not succeed, all you get is Null instead, which evaluates to False and thus you can do something about it, error out or whatever you want.On that note, if all your doing is checking if something is of a specific type, this will do: If Not SomeType(x) Then Throw "error"The reason for putting the result in a variable is because usually one has to use the object, not just test it. What happens if I use a field in the Function that the original type didn't have? The compiler would complain before it even managed to run it ;) Since all types must be known at compile-time.Not something you generally have to worry about, so long as you always check that your casts succeed. |
| ||
@ field in the Function If the cast local myType:generic = generic(g) failed, then myType is "null". So accessing "myType.yawn" results in a segfault (an access to a null-object). Surely similar things happened to you already - loading non-existing images or so. This is the same thing here. PS: "myType" is just a variable name I have choosen randomly. So name it as you like. bye Ron |
| ||
Okay, I think i got it all now. Thanks guys! =) |
| ||
Never mind, disregard my last post, I don't got this. I'm such a idiot, because in my test code I made two variables with the same type. Like this, buyer:generic = New generic list_objects.AddLast buyer notbuyer:generic = New generic list_objects.AddLast notbuyer I tried to get it to work like this, but I can't figure out how to convert the types. Function doesnothing (g:Object) Local myType:Master = Master(doessomething (g)) If Not myType Then Throw "passed invalid type" Print "yawn:" +myType.yawn End Function Function doessomething:Object (g:Object) Local myType1:generic = generic(g) If myType1 Then Local masterType1:Master = Master (myType1) Return masterType1 Else Local myType2:namebrand = namebrand(g) If myType2 Then Local masterType2:Master = Master (myType2) Return masterType2 EndIf EndIf End Function Sorry for the trubble. |
| ||
Without knowing more about your types its hard to determine if this should work or not, specifically which types extend which, if at all. You are also casting several times which is unnecessary. Also, your doessomething function is kinda useless, it could just as well be coded this way: Function doessomething:Object (g:Object) Return Master(g) End FunctionWhich makes it doubly useless. But again, not 100% sure without more information. Maybe you should explain what you are trying to do. Is there some specific reason you are doing it this way or is it just for testing the language? EDIT: Casting isnt something one generally do a whole lot, since one has Methods that can work on a class implicitly, which mitigates the need for the most part. |
| ||
I was about to say the same thing: why are you asking and what are you actually trying to accomplish? There's a whole lot of discussion suggestions to do unusual things here, but chances are there's a simpler and cleaner way to achieve what you want. |
| ||
Well in my game I use Types for the enemies, and I use a different Type for each kind of enemy (turrets,infantry). They all use some of the same basic Functions, but because they use different Types I have to make multipol Functions that do the exact same thing. I know that I can pass the needed info as individual variables, but that can get messy, and things like removing elements(?) need the Type itself. The later is is the big reason. And here are the types I have been using in my test, sorry for that confusion. Type generic Field yawn EndType Type namebrand Field yawn EndType Type Master Field yawn EndType |
| ||
I think it would help if you better understood the concepts of Object Oriented programming. Here's something about subtyping - https://en.wikipedia.org/wiki/Subtyping In BlitzMax a type can inherit attributes (fields and methods) from its super type. For example, you might do: Type Master Field yawn EndType Type namebrand Extends Master End Type Here, an instance of namebrand inherits the field "yawn" from its parent/super type. There are many tutorials online about object oriented programming, polymorphism, inheritance, etc, that will probably give you a better understanding of the things you can achieve with it. They are all features that you appear to be wanting to use. |
| ||
I suspected as much. What you are doing is essentially object oriented, but your doing it manually. This is what inheritance and virtual methods are all about! Your enemies probably has some common fields and functionality that can be expressed quite nicely with OOP. If Object Oriented Programming (OOP) is new to you, i suggest reading up on it, as it can be involved for newcomers. A small example: Type Enemy Field X,Y Method Move( x,y) Self.X :+ x Self.Y :+ y EndMethod Method Die() Print "enemy is dead" EndMethod EndType Type BigEnemy Extends Enemy Method Die() Print "big enemy is dead" EndMethod EndType Local e1:Enemy = New Enemy Local e2:Enemy = New BigEnemy e1.Die() e2.Die()Doing this by hand would look something like this: Type Enemy Field X,Y EndType Type BigEnemy Field X,Y EndType Function Move( e:Object, x, y) If Enemy(e) Then Local ee:Enemy = Enemy(e) ee.X :+ x ee.Y :+ y Elseif BigEnemy(e) Then Local be:BigEnemy = BigEnemy(e) be.X :+ x be.Y :+ y EndIf EndFunction Function Die( e:Object) If Enemy(e) Then Print "enemy is dead" Elseif BigEnemy(e) Then Print "big enemy is dead" EndIf EndFunction Local e1:Enemy = New Enemy Local e2:BigEnemy = New BigEnemy Die(e1) Die(e2) EDIT: Added second sample |
| ||
Ah, that was so simple! I think I've got it this time. And I will be sure to look into Object Oriented Programming. Thank you all for your help. Bye for now. |