About Classes and Variables
Monkey Forums/Monkey Beginners/About Classes and Variables
| ||
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! |
| ||
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. |
| ||
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 |
| ||
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. |
| ||
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? |
| ||
The button doesn't know where the app is. Try something like the following (not tested so it may not be exactly right): |
| ||
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! |
| ||
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. |
| ||
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. |
| ||
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] |
| ||
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 |
| ||
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! |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
Yes this is a way of doing it. |
| ||
Except that the Return statement should have come after the increment! I fixed it now. |
| ||
Thanks a lot! I'm really lerning a lot here |
| ||
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 :) |
| ||
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 |
| ||
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! |
| ||
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() |
| ||
You could also use reflection. |
| ||
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 |
| ||
Thanks a lot for the tips! I'll use what you say |
| ||
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!! |
| ||
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. :) |
| ||
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. |
| ||
Great! thanks for sharing! |