Page 1 of 1

Organizing Variable Scope (also, a Map/Linked List Question)

Posted: Tue Apr 03, 2012 9:02 pm
by Zach
Sorry I really couldn't think of a better title that would get my point across and look interesting enough for people to click 8)

I know I have asked how people lay out their programs in the past, but I ran into a problem that is bugging me and I wanted to see how others would handle it.
I guess this is a "What would Jesus do?" question of sorts :mrgreen:

I like to try and keep my code fairly organized, like a lot of people of course, which means I have a lot of variables kept inside the procedures that use them. So far this is mainly to do with my GUI code, as I am creating Gadgets by using #ProGUI_Any and storing the returned IDs in a variable named for the Gadget. A lot of it is Text Labels and String Gadgets for user input, and I will definitely have several hundred by the time the GUI itself is finished. There will be some other things like buttons and such too (just added two last night).

The issue I am concerned with is being able to access those variables either from my Event Loop for response to input, or from within other procedures themselves which may act on and change the state of the Gadget.

Right now I have the variables defined within the procedures, like I mentioned.. Using Protected just to be "neat" about it, however when I need to access one or several I end up changing them to Global, although I am trying to keep in mind that "good practice" says avoid Globals unless you really need them. I apparently can't use "Shared" unless I define the variable outside the procedures, in the main source file - which wouldn't necessarily be a problem for me, but I don't think I would be able to see them from other source files.... ? Since my code is compartmentalized to the best of my ability, I'm wondering if I just have to use a lot of globals or if there are other ways to reference variables inside procedures; I thought about pointers but that is messy and I think I would make things worse if I tried to do that..

Has the phobia of "Globals" being used a lot / looking "bad" to others who might read my code been wrongly instilled in me? :oops:

Re: Organizing Variable Scope (?)

Posted: Tue Apr 03, 2012 9:41 pm
by Nituvious
I use the define keyword for non-global variables but only because I use EnabledExplicit now.

Re: Organizing Variable Scope (?)

Posted: Tue Apr 03, 2012 10:31 pm
by Zach
Yeah I also work with Enable Explicit, and I think it has helped me to be better at keeping track of stuff, but I still run into little hiccups like these where I wonder what's best..

I forgot to add to my OP.. I'm also considering that I am prolly gonna need to rewrite a few things.. As for example, I wanted a button to reset all the forms on the visible page so I added it, and then realized I'd have to add all my forms manually to the procedure to clear them.... not fun.

So that was another question, now that I remembered.. Is it possible to use a Map or Linked List or something of that sort, so that all I would have to do to change the state of a Gadget is iterate the Map / Linked List ? That seems like the best solution for me, since I will have hundreds of StringGadgets in use.. Figured I would ask if anyones done it before I waste a day messing around trying to make it work :|

I wonder if that also wouldn't solve some of these scoping issues, but again I don't know if its possible to catch events by referencing the gadget through a Map or Linked List in the event loop??

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Tue Apr 03, 2012 10:49 pm
by skywalk
Use #PB_Any when creating your gadgets and store the results in a structured array or list or map.
Whichever you are familiar with.
EventGadget() returns a gadget #, the same value returned when you 1st create the gadget.

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Tue Apr 03, 2012 11:22 pm
by Blood
Zach wrote:Sorry I really couldn't think of a better title that would get my point across and look interesting enough for people to click 8)

I know I have asked how people lay out their programs in the past, but I ran into a problem that is bugging me and I wanted to see how others would handle it.
I guess this is a "What would Jesus do?" question of sorts :mrgreen:

I like to try and keep my code fairly organized, like a lot of people of course, which means I have a lot of variables kept inside the procedures that use them. So far this is mainly to do with my GUI code, as I am creating Gadgets by using #ProGUI_Any and storing the returned IDs in a variable named for the Gadget. A lot of it is Text Labels and String Gadgets for user input, and I will definitely have several hundred by the time the GUI itself is finished. There will be some other things like buttons and such too (just added two last night).

The issue I am concerned with is being able to access those variables either from my Event Loop for response to input, or from within other procedures themselves which may act on and change the state of the Gadget.

Right now I have the variables defined within the procedures, like I mentioned.. Using Protected just to be "neat" about it, however when I need to access one or several I end up changing them to Global, although I am trying to keep in mind that "good practice" says avoid Globals unless you really need them. I apparently can't use "Shared" unless I define the variable outside the procedures, in the main source file - which wouldn't necessarily be a problem for me, but I don't think I would be able to see them from other source files.... ? Since my code is compartmentalized to the best of my ability, I'm wondering if I just have to use a lot of globals or if there are other ways to reference variables inside procedures; I thought about pointers but that is messy and I think I would make things worse if I tried to do that..

Has the phobia of "Globals" being used a lot / looking "bad" to others who might read my code been wrongly instilled in me? :oops:
This is not a troll but this is exactly one of the problems OOP solves. Look into it.

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Wed Apr 04, 2012 12:29 am
by Zach
Yes, I realize OOP makes some of this stuff a little easier / cleaner, whatever you want to term it..

But I'm not ready to jump into something like C++ or C# or any of that.. probably not for a few years or at least until I get some completed programs under my belt.
Just the way I want to do it.. But then again I may never... unless it were to write something for a PB program.. I just really like PB, and I'd like to use it and learn it to its fullest possibilities.


Skywalk,
Yes that is pretty much what I was asking about, if it were possible.. So it seems it is based on your post, so I will take a crack at it next time I sit down to code 8)

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Wed Apr 04, 2012 1:14 am
by Demivec
Zach wrote:Right now I have the variables defined within the procedures, like I mentioned.. Using Protected just to be "neat" about it, however when I need to access one or several I end up changing them to Global, although I am trying to keep in mind that "good practice" says avoid Globals unless you really need them. I apparently can't use "Shared" unless I define the variable outside the procedures, in the main source file - which wouldn't necessarily be a problem for me, but I don't think I would be able to see them from other source files.... ? Since my code is compartmentalized to the best of my ability, I'm wondering if I just have to use a lot of globals or if there are other ways to reference variables inside procedures; I thought about pointers but that is messy and I think I would make things worse if I tried to do that..
You have several options, in addition to any that have already been mentioned. These options involve structures. You would define a structure variable, i.e. GUI.gadgets, an then share this variable into each of the procedures that need access to those things. It wouldn't be global but would exist at the main level and in the specific procedures that need access to it.

The structured variable could contain a list, an array, or a map to track each gadget (as mentioned by skywalk).

You can make it as sophisticated or simple as you like.


Another option is to use a procedure parameter as a pointer to a structure. Since you wanted to avoid pointers, this would not be something you would want to try. It is useful however for procedures that call other procedures, such as event handlers for specific gadget types. You can pass a pointer to the data for the specific gadget (such as it's address in a structured array of gadgets) and reference things without have to pass each of them as a parameter.

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Wed Apr 04, 2012 1:29 am
by IdeasVacuum
If the gadgets are not created on the fly at run time, then you could use a list of constants with meaningful names rather than PB_Any or hard-coded numbers.

Don't be afraid to use Global variables that Procedures can Share, again with meaningful names they work well and your code will be easy to understand when you return to it.

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Wed Apr 04, 2012 3:39 am
by Demivec
IdeasVacuum wrote:Don't be afraid to use Global variables that Procedures can Share, again with meaningful names they work well and your code will be easy to understand when you return to it.
You don't have to 'share' a 'global' variable. :mrgreen:

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Wed Apr 04, 2012 3:56 am
by IdeasVacuum
...indeed you don't need to use 'Shared' with global vars, only with non-global vars which are not defined/declared within a Procedure. :oops:

...however, using 'Shared' with global vars does no harm, and can visually differentiate between vars created inside a Procedure and those created outside the same Procedure, and serves as a reminder that the value could be changed elsewhere.

Of course a good naming convention helps too, for example I would use iTotalWidgets to indicate the var is an int, or igTotalWidgets to indicate it is a global var.

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Wed Apr 04, 2012 2:36 pm
by Zach
Thanks, lots of interesting solutions to consider..

I did use Constants at first, but then I got sick of writing them, and then referencing them, and then writing some more... I was very glad to get turned on to the "xyz = Gadget(blahblah)" method :mrgreen: They are OK for small simple programs, but this one will be far too large, having dozens and dozens of input fields to track and manage.

I like the idea of trying a structured array/list/whatever first..


I guess I should finally show something too.. This is what I have been working on, an hour here and an hour there.. Lots more to code, it's only 5% done
Image

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Thu Apr 05, 2012 7:24 pm
by Env
The way I would personally do it:

Have a structure for a particular window that contains fields for each gadget as well as the window. Create a global variable with the structure which will be used to access the gadget/window id's as well as any additional information you want to store. Have a procedure to open and to handle events. Have a basic event loop that will direct the event to the relevant window's event receiver.

Code: Select all

; - Window Data -
Structure formMainStruct
    wndID.l
    ctlButton1.l
    ctlButton2.l
    ctlButton3.l
EndStructure

Global formMain.formMainStruct

; - Open the Window -
Procedure formMain_Open()
    With formMain
        
        \wndID = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 300, 130, "Sample Stuffs", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
        
        \ctlButton1 = ButtonGadget(#PB_Any, 10, 10, 280, 30, "Button 1")
        \ctlButton2 = ButtonGadget(#PB_Any, 10, 50, 280, 30, "Button 2")
        \ctlButton3 = ButtonGadget(#PB_Any, 10, 90, 280, 30, "Button 3")
        
    EndWith
EndProcedure

; - Event Handling -
Procedure formMain_HandleEvents(Event)
    With formMain        
        Select Event
            Case #PB_Event_CloseWindow
                End                
            Case #PB_Event_Gadget                
                Select EventGadget()                        
                    Case \ctlButton1
                        Debug "Button 1 Pressed"                        
                    Case \ctlButton2
                        Debug "Button 2 Pressed"
                    Case \ctlButton3
                        Debug "Button 3 Pressed"
                EndSelect
        EndSelect
    EndWith
EndProcedure

; - Open Window -
formMain_Open()

; - Run Main Loop -
Define.l lEvent
Repeat
    lEvent = WaitWindowEvent()
    Select EventWindow()
        Case formMain\wndID
            formMain_HandleEvents(lEvent)
    EndSelect
ForEver

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Thu Apr 05, 2012 8:18 pm
by Zach
That is an interesting idea.. Maybe a little too complicated for my feeble brain, but I appreciate your sharing it.
I had thought of something slightly similar before, but I think I can pull a good idea or two out of this as well.

I should also point out, I am using ProGUI so the whole multiple windows thing would probably end up being more complex than necesarry.. The great thing about ProGUI's PanelEx gadget is I can build each individual page under its own Procedure and localize a lot of the code that way, and I just use events to switch between which page is displayed.

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Sat Apr 07, 2012 10:41 pm
by Zach
I had time to play around with a Map yesterday and successfully added a TextLabel gadget to the Map, and had it show up like I expected.
So I as long as everything else plays nice this will be a really simple, easy to use solution.

I just have to get a quick Macro written so I'm not throwing out "AddMapElement" commands every other line :lol:

Re: Organizing Variable Scope (also, a Map/Linked List Quest

Posted: Sun Apr 08, 2012 1:59 am
by skywalk
You don't have to use AddMapElement() to expand the Map.

Code: Select all

NewMap myMAP$()
myMAP$("1st") = "Add Something" ; Add element
; Use AddMapElement() requires 2 statements
AddMapElement(myMAP$(), "1st")  ; Using same MapKey will overwrite any previous entries
myMAP$() = "Overwrite Something"
myMAP$("2nd") = "Try again"     ; Add element
ForEach myMAP$()
  Debug myMAP$()
Next