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

Everything else that doesn't fall into one of the other PB categories.
Zach
Addict
Addict
Posts: 1677
Joined: Sun Dec 12, 2010 12:36 am
Location: Somewhere in the midwest
Contact:

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

Post 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:
Last edited by Zach on Tue Apr 03, 2012 10:34 pm, edited 1 time in total.
Nituvious
Addict
Addict
Posts: 1030
Joined: Sat Jul 11, 2009 4:57 am
Location: United States

Re: Organizing Variable Scope (?)

Post by Nituvious »

I use the define keyword for non-global variables but only because I use EnabledExplicit now.
▓▓▓▓▓▒▒▒▒▒░░░░░
Zach
Addict
Addict
Posts: 1677
Joined: Sun Dec 12, 2010 12:36 am
Location: Somewhere in the midwest
Contact:

Re: Organizing Variable Scope (?)

Post 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??
User avatar
skywalk
Addict
Addict
Posts: 4242
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

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

Post 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.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Blood
Enthusiast
Enthusiast
Posts: 161
Joined: Tue Dec 08, 2009 8:34 pm
Location: United Kingdom

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

Post 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.
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto is never necessary, and in practice it is almost always easy to write code without it. We have not used goto in this book. -- K&R (2nd Ed.) : Page 65
Zach
Addict
Addict
Posts: 1677
Joined: Sun Dec 12, 2010 12:36 am
Location: Somewhere in the midwest
Contact:

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

Post 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)
User avatar
Demivec
Addict
Addict
Posts: 4281
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

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

Post 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.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

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

Post 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.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Demivec
Addict
Addict
Posts: 4281
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

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

Post 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:
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

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

Post 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.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
Zach
Addict
Addict
Posts: 1677
Joined: Sun Dec 12, 2010 12:36 am
Location: Somewhere in the midwest
Contact:

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

Post 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
Env
Enthusiast
Enthusiast
Posts: 151
Joined: Tue Apr 27, 2010 3:20 pm
Location: Wales, United Kingdom

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

Post 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
Thanks!
Zach
Addict
Addict
Posts: 1677
Joined: Sun Dec 12, 2010 12:36 am
Location: Somewhere in the midwest
Contact:

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

Post 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.
Zach
Addict
Addict
Posts: 1677
Joined: Sun Dec 12, 2010 12:36 am
Location: Somewhere in the midwest
Contact:

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

Post 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:
User avatar
skywalk
Addict
Addict
Posts: 4242
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

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

Post 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
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Post Reply