About Classes and Variables

Monkey Forums/Monkey Beginners/About Classes and Variables

eNano(Posted 2014) [#1]
Hi, this doubt is killing me:
How can I communicate a value from a class to another?
For example suppose I have a class to control touch features and another class to draw and update buttons, how can the button class read the x and y coords from the touch class without having to put this data as a parameter? something like a global data for all the classes (I hope is clear what I said)
Thanks!


Jesse(Posted 2014) [#2]
well, since the the touch control class is going to be a singleton, you can create an instance of the touch class as a global.
Example:
Global touch:ControTouch = new ControlTouch
...
touch.Update()
...
pos.x = touch.x ' or touch.GetX()


just one of many possibilities.


Supertino(Posted 2014) [#3]
Does this help?

Class button
   Method Render:Void()
      DrawText myTouch.x,10,10
   End Method
End Class

Class touch
   Field x:Int
   Method Update:Void()
      x = mousex()
   End Method
End Class

Global myButton:button = new button
Global myTouch:touch = new touch

myTouch.Update
mybutton.Render



Gerry Quinn(Posted 2014) [#4]
Yes, there are three fairly standard ways you might do it:

- make the touch class a global

- put an instance of the touch class in a single global (typically theApp) that things like buttons can access

- put a touch class field in the button class, and initialise it when the button is created

The third is not a great idea here as it involves a lot of busywork just to avoid a single global. If you find yourself doing this a lot you may be suffering from globophobia.


eNano(Posted 2014) [#5]
Thanks a lot for helping me! I think this kind of stuffs is what I need but I still have problems putting all together because I have errors
For example I have more or less this:
Import touch ' (the touch class is in other file)
Import button ' (the button is also in a gui file)

Class test Extends App 
    Global myTouch.touch = new touch
    button.printCoords() ' A function on the button class just for test
End Class


And in the button class I have something like
Class button
    Field bx
    Field by
    Field ...
    Function printCoords()
        print myTouch.x
        print myTouch.y
    End Function
End Class


but when I run all it says: Identifier 'myTouch' not found
what is wrong?


Gerry Quinn(Posted 2014) [#6]
The button doesn't know where the app is.

Try something like the following (not tested so it may not be exactly right):




eNano(Posted 2014) [#7]
Thanks a lot that works! but my head is more scrambled now I'll have to seat a while to clean up my ideas
Thanks to all for your help!


Jesse(Posted 2014) [#8]
See if this makes more sense to you:

Import touch ' (the touch class is in other file)
Import button ' (the button is also in a gui file)

Global myTouch:touch '***************************

Class test Extends App 
    myTouch = New touch
    button.printCoords() ' A function on the button class just for test
End Class

Class button
    Field bx
    Field by
    Field ...
    Function printCoords()
        Print myTouch.x
        print myTouch.y
    End Function
End Class



but you need to have "Global myTouch" in the "touch file" outside of the "touch" class
and you need to have the "button" file import the "touch" file. This way you make all of the code interdependent but only related files. That is, files that depend on each other.


Gerry Quinn(Posted 2014) [#9]
Yes, I only put the touch object in the app because that was the way eNano seemed to be aiming for, It will work fine as its own global too. And it will also work as an ordinary field in TestApp.

Actually I'd recommend initialising something like myTouch in app.OnCreate() anyway, because it seems like the sort of thing that might make mojo calls, even if it doesn't at the moment. If it needs to check screen size or something, it may fail in its current form.


consty(Posted 2014) [#10]

How can I communicate a value from a class to another?



As other people suggested there are many ways, two of the most useful ways are.

1. Make some stuff global
If you want to avoid flooding your application global scope you can create classes that wrap other globals. This way you get your code better organized.

2. Use singletons
This is a very handy design pattern to have a class self initialized and self contained that way you will get more logical approach to your code.

You can play with these two options and see what is better.

Here is an example to show that you can do lots of crazy stuff with code operations.
[CODE]
Strict

Class GlobalStuff
Global TheForm:Form
End

Class SomethingChanges
Method ChangeThem:Void(name:String)
Local n:Int = 1
For Local i:IControl = Eachin GlobalStuff.TheForm.controls
i.SetName(name + " " + n)
n += 1
Next
End
End

Class SomethingClicks
Method ClickThem:Void()
For Local i:IControl = Eachin GlobalStuff.TheForm.controls
i.Click()
Next
End
End

Function Debug:Void(message:String)
Print(message)
End

Function Main:Int()

' Create a form
GlobalStuff.TheForm = New Form
' Create a local variable reference as well
' (optional step) only as typing convinience
Local form:Form = GlobalStuff.TheForm

' Create two buttons
Local button1:Button = New Button
Local button2:Button = New Button

' Have these two buttons attached to the form
' do not be confused with the "form" variable which is local.
' In reality "GlobalStuff.TheForm" variable will prevail
' Because it's global, outside this scope the local variable
' will be inexistent.
form.AddControl(button1)
form.AddControl(button2)

' Create some helper dummy classes
Local changer:SomethingChanges = new SomethingChanges
Local clicker:SomethingClicks = new SomethingClicks

' Perform the name changing operation
' normally the most easy and direct approach would be to do
' something like this right on the fly
' button1.name = "Button 1"
' button2.name = "Button 2"
' but because we like more complex approaches an alternative
' way of doing things will be preferred. :P
changer.ChangeThem("Button")

' Same as well for performing the click events
clicker.ClickThem()

Return 0
End


Class Form
Private
Field controls:List<IControl>

Public
Method New()
controls = new List<IControl>
End

Method AddControl:Void(c:IControl)
controls.AddLast(c)
End
End

Interface IControl
Method SetName:Void(name:String)
Method Click:Void()
End

Class Button Implements IControl
Private
Field form:Form
Field name:String

Public
Method SetName:Void(name:String)
Self.name = name
End
Method Click:Void()
Debug("Clicked " + name)
End
End
[/CODE]


eNano(Posted 2014) [#11]
Thanks a lot!! You've shared very cool stuffs, I'll practice with this concepts to learn some more. I've been playing with Blitz basic for a while and this kind of concepts are new for me


eNano(Posted 2014) [#12]
Hi!
I have another question regarding class instances:
Is possible to get some handle or pointer for a particular instance of a class?
I really need to imitate the blitz3d feature of Types handles, because is really useful to store integers to call a specific instance without doing a cycle to each one
Thanks a lot!


Samah(Posted 2014) [#13]
You could use a global IntMap I guess...
Class Foo
  Global nextID:Int = 0
  Global instances:IntMap<Foo> = New IntMap<Foo>

  Field id:Int = 0

  Method New()
    id = nextID
    nextID += 1
    instances.Set(id, Self)
  End
End

Function Main:Int()
  Local f1:Foo = New Foo
  Local f2:Foo = New Foo
  Local f3:Foo = New Foo
  
  Local idx1:Int = f1.id
  Local idx2:Int = f2.id
  Local idx3:Int = f3.id
  
  Local bar:Foo = Foo.instances.Get(idx3)
  
  Print bar.id
End


Edit: Note that this will keep a reference to the object, preventing Monkey's automatic garbage collector from collecting it. When you no longer want the object you'll have to manually remove it from the global IntMap.


Gerry Quinn(Posted 2014) [#14]
Can't you just use the name of the object? If I say:

local aThing:Thing = New Thing()


..then under the hood aThing is nothing more than a pointer to the instance that was created.

If you need to remember arbitrary objects, just make a collection of Objects, and it will happily hold aThing:Thing or bOtherThing:OtherThing, since all objects derive from the Object class.


eNano(Posted 2014) [#15]
Thanks a lot Samah! I'll try to do that I'm still learning the basics of this great language as you can see.
Thanks Gerry Quinn for the answer, what I'm trying to do is some kind of pick buffer (I don't know how to call it or if such of thing exist in other way, I'm rookie on this)
I'll try to explain what I need to do:
basically I draw in a secondary buffer an area painted with a specific rgb color that represents the limits of a pickable GUI gadget, so when I pick on the screen I read the color in that coords, I convert the color into an integer and then I call an object with that pointer.
Suppose I have an Integer value of 6560035 which represents the pointer to an object
I convert the integer to an rgb value with this function (learned from blitz community):
Function getChannel ( rgbValue%, channel% = 0 )
	Local R=1
	Local G=2
	Local B=3
	Select channel
		Case R
			Return rgbValue Shr 16 And %11111111
		Case G
			Return rgbValue Shr 8 And %11111111
		Case B
			Return rgbValue And %11111111
	End Select
End Function

This function gives me (100,25,35)
Then I draw a filled shape with that color in an invisible buffer
When I do a picking I get the color in that point of (100,25,35)
So I convert the picked rgb values into an integer
Function rgb:int ( rValue:int = 0, gValue:int = 0, bValue:int = 0 )
	rValue = rValue Shl 16
	gValue = gValue Shl 8
	bValue = bValue
	Return rValue Or gValue Or bValue	
End Function

This function gives me 6560035 which is the direct pointer to an object, so I can get it directly (I don't know how in Monkey) without doing an iteration
I don't know if this is efficient but It gives me the possibility of doing complex pickable items in an app without too much effort and having an integer gives me many other possibilities too.


consty(Posted 2014) [#16]

I really need to imitate the blitz3d feature of Types handles



Blitz3D by design was not based on classes and objects, it used this concept of "handles" to represent variables that were initialized after a type. When you created a variable like this the best way it could be represented was as an integer letting the actual data structure somewhere in memory.

In Monkey since there are classes and objects, this concept of using pointers is obsolete, because an object variable is a strong type and represents itself. This makes use of pointers/handles obsolete not because they are bad, but simply because they are not useful.

In practical terms, let's say you have a list for your enemies.
Strict

Class Enemy
End

Function Main:Int()
	Local enemyList:List<Enemy> = New List<Enemy>
	
	For Local i:Int = 0 To 9
		Print("Creating enemy: "+i)
		enemyList.AddLast(New Enemy)
	Next
	
	Print("Created total: " + enemyList.Count() + " enemies")	
	
	Return 0
End


In this case when you initialize an Enemy object you will always know that is an object derived from the Enemy class. If it happens something weird with this data the compiler will complain and save you from unexpected errors.


Gerry Quinn(Posted 2014) [#17]
Okay, basically you are just looking for an arbitrary unique integer associated with each object. One solution would be to assign each pickable object a unique integer on its creation:

Global uniqueInt:Int
Function GetUniqueInt:Int()
uniqueInt += 1
Return uniqueInt
End

Store one of those in or with your object, and you can use it just like the handle you used in Blitz.


consty(Posted 2014) [#18]
Yes this is a way of doing it.


Gerry Quinn(Posted 2014) [#19]
Except that the Return statement should have come after the increment! I fixed it now.


eNano(Posted 2014) [#20]
Thanks a lot! I'm really lerning a lot here


Salmakis(Posted 2014) [#21]
Oh, i remember when i was changing form blitz3d to blitzmax, and how confusing the concept of object oriented programming was to me at the begin,
it made no sence and was hard to understand, but Keep trying and you will see that everything is so much easier and the possibilites are unlimited when you get behind the stuff of objects and classes.
then you will see that it is in most cases (99,9%) not neccesary to get any objecthandles or something. (i also tried that in the begin)
if you make a local, global or field as an object like:
Global Player1:Player, then you dont Need any number or handle, since you can allways just refer to Player1, call ist stuff with Player1.bla() or acces ist Content with
hp = Player1.HitPoints, or even compare if an item from a list wich you actually iterating is the Player ( if Player1 = bla then ....)

I remember the concept of the Color to handle thingie, since i used it by myself once ago, by painting Areas of the gui in a Color wich is then dedicated to a button/gadged, but here for Buttons and so on, you could / should just put them all into a list, iterate this list and ask for every button if the mouse/touch Position is in the boundaries of this button, its not much Iteration, even if you have like 100 Buttons it will be done in <1ms in most cases.
Also you can order your Buttons into a window or hud class, wich itself is checking if the mouse is even inside this window/hud before checking each button.
Speciall since on mobile Targets as example you never know wich Resolution is used, so you cant deploy a graphic for each Resolution if you predraw them, also ist very static and annoying to modify/redraw the graphic/graphic/s, or if you create them on runtime this can be very time consuming on some plattforms, every time you add a button into your gui/hud or Change the menu and so on.

Just think that now you can pack objects into other objects, let objects have ist own lists or fields of Subobjects (childs) and so on
just try to not copy your blitz3d mechanics, try think new :)


eNano(Posted 2014) [#22]
Thanks a lot Salmakis for your support! I was suspecting that I should put my brain on soapy water for a while but you just confirmed that.
I know this is going to take a while for me to get it but I'll keep trying!
This is a really interesting world, I've started playing with Qbasic when I was a kid and a big jump was Blitz, I think is time to jump again. The people of this community is really constructive and helpful, you should open a topic with challenges for beginners with simple tasks to let us feed from our mistakes


eNano(Posted 2014) [#23]
Another question:
Is it possible to assign a function or a method to a variable or field?
suppose you have a test() function and you have a class.field
can you do something like class.field=test() and then call the function from the class.field? (or any approach to this concept)
Thanks!


Jesse(Posted 2014) [#24]
Not possible but you can create a class with that function and then assign that class to the
field variable:
field := New testClass
field.test()


Raph(Posted 2014) [#25]
You could also use reflection.


Samah(Posted 2014) [#26]
can you do something like class.field=test() and then call the function from the class.field? (or any approach to this concept)

You can get a reference to the field using reflection, then get the value from that.
#REFLECTION_FILTER = "*"
Import reflection

Class Foo
	Field bar:String = "hello"
End

Function Main()
	' get a reference to the class
	Local ci:ClassInfo = GetClass("Foo")

	' get a reference to the field
	Local fi:FieldInfo = ci.GetField("bar")

	' an instance of Foo
	Local f:Foo = New Foo

	' get the value
	Local val:String = UnboxString(fi.GetValue(f))
	Print val
End



eNano(Posted 2014) [#27]
Thanks a lot for the tips! I'll use what you say


eNano(Posted 2014) [#28]
Hi! another conceptual question:
Is a bad idea to create many classes or is not much weight at runtime?
I'm thinking about creating a sort of "commands" to call them inside the app, suppose you have 100 commands (just to put a number), is insane to think of a class for each one? ( I'm trying to do a mobile app)

Thanks a lot!!


Samah(Posted 2014) [#29]
Class definitions are pretty lightweight, it's more about how many objects you have in memory and the size of those objects.
100 classes is not insane. 1000 might be overkill. :)


Gerry Quinn(Posted 2014) [#30]
Nothing wrong with 1000 classes either. In some targets, such as C++, the number of classes is completely irrelevant to the resulting code. In others, such as Java, an object is constructed for each new class that is referenced. But either way, they are not any significant drain on the system.


eNano(Posted 2014) [#31]
Great! thanks for sharing!