Passing more than one Type to a Function

BlitzMax Forums/BlitzMax Programming/Passing more than one Type to a Function

Rooster(Posted 2017) [#1]
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.


grable(Posted 2017) [#2]
Whats wrong with using Object? Either that or some common base class.


Rooster(Posted 2017) [#3]
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?


Derron(Posted 2017) [#4]
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


grable(Posted 2017) [#5]
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.


Rooster(Posted 2017) [#6]
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).


grable(Posted 2017) [#7]
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)



Rooster(Posted 2017) [#8]
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! =)


Bobysait(Posted 2017) [#9]
 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 )



grable(Posted 2017) [#10]
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.


Derron(Posted 2017) [#11]
@ 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


Rooster(Posted 2017) [#12]
Okay, I think i got it all now.

Thanks guys! =)


Rooster(Posted 2017) [#13]
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.


grable(Posted 2017) [#14]
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 Function
Which 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.


FireballStarfish(Posted 2017) [#15]
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.


Rooster(Posted 2017) [#16]
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



Brucey(Posted 2017) [#17]
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.


grable(Posted 2017) [#18]
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


Rooster(Posted 2017) [#19]
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.