Announcement

Collapse
No announcement yet.

Globals again

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Globals again

    I am old fashioned but trying to understand the aurgument about GLOBAL's, it seems its all about documentation, OK. But then I have to question the lengths some programmers go to not to use them. A common recommeded trick seems to be put the data or a pointer to the data into the USER sections of a window or control. But that is a poorly documentable GLOBAL ie accessable from anywhere in the program while the window or control exists (I wouldn't even want to think about what happens when the window or control does not exist when the data is requested).
    So effectively they create a GLOBAL that can't be addressed by name only by index and can only contain a LONG so if it is not a LONG you are after it has to be a pointer.
    Just how badly documentable and dangerous can you get? The data it points to has to be persistant ie STATIC not a LOCAL which is on the stack as it will most likely be out of scope and destroyed before you try to access it (GPF almost gaurenteed)!!
    To me it seems the choice between sensible documented use and hard to document dangerous methods just to be proud you live by the the rule of no GLOBALS

  • #2
    A database is also a global entity..
    It's hard to explain why you should or shouldn't.

    Once you have a global it's hard to get rid of.
    The problem arises when you need a 2nd global doing a similar thing.

    An array can help you but then you'll need another way to make it unique.
    First you did that by using a single global, using the array you may need an unique name but.. where do you store the unique name to refer later on to?

    You see, and then you point about the hWnd User stuff, it may be a design flaw to destroy the data via the window, who knows?

    In fact the user part does not clean up pointers/memory so the memory is safe by itself bt then where to store the pointer?

    I am just telling you you'll need some mechanic to find the specific memory again.
    Using a global is usually a poor and easy decision.
    hellobasic

    Comment


    • #3
      A lot of it depends on what you are going to do with your global and yes, it is all about documentation. Naming conventions help to prevent some issues.

      Globals are a tool, and as long as the tool is used wisely, there is nothing wrong with them, just like any other tool.

      But it's a poor work man that blames the tool.
      Rod
      I want not 'not', not Knot, not Knott, not Nott, not knot, not naught, not nought, but aught.

      Comment


      • #4
        Originally posted by John Petty View Post
        I am old fashioned but trying to understand the aurgument about GLOBAL's

        Variable Scope. Global scope means the variable as around while the application is running. This can create bugs as the following shows:

        The following program demonstrates:
        Code:
        #Compile Exe
        #Dim All
        
        Global i As Long
        
        Function DisplayCountdown() As Long
            'local i as long
            For i = 3 To 1 Step -1
                MsgBox Str$(i)
            Next i
        End Function
        
        Function PBMain () As Long
        
            i = 99
            MsgBox "i should = 99: =" & Str$(i)
            DisplayCountDown
            MsgBox "i should = 99: =" & Str$(i)
        
        End Function
        It is simple to forget to define a local variable when writing a new function. If a global exists with the same name, it will compile and run. It will be difficult to find that your global is being set in some unrelated function. The new feature works fine, but old features using the global break and when the break only occurs when this new feature is used. It could take a long time to find a the steps where it always breaks.

        If you work in a team or use an include file written by someone else you may have Global variable name collisions. Two people could decide to use the same name for a variable. (Count, FirstNames, DatabaseID, hWndMainForm, etc...) In PB 9 if the variable is defined as the same type you won't even know it is defined elsewhere.

        Code:
        Global i as Long 
        Global i as Long ' Won't give duplication name definition
        Global i as String ' Will give duplicate name definition
        Code Maintenance. When I was working on a project that had 12 years of code (Legacy code) by many different programmers over the years there were many globals. Some globals did the same thing used for the same purpose. A programmer would add a new global to avoid messing with the existing global. The new global (Counter2) set it to the value of Counter (The original) and now you can freely change your own global for that new feature your adding without causing any bugs of the old features using the other global. Since you always set the value to the original before you use it, you know it will have the value of the other "global". The programmer would get there new feature working and when figure out when it was necessary set the original counter to the counter2 value (usually at the end of there feature being run, but sometimes it was also required in the middle). It was really ugly. This created a lot of bloat and hard to maintain software. Globals breed more globals. This actually became a problem because the application ran out of global variable space. I should also point out, in some cases the type of the global changed. The old global might have been Integer and a Long was required for the new features. In some cases the global wasn't really global, but just defined that way just to avoid passing a parameter to a function.

        Making a local variable global is easily. Try it. Move the Local i as long to the top of the program and change to word Local to Global. Done. (Oh, you might want to change the name of the variable to something more unique, but this is also simple since there is only one function using the varialbe).

        Making a global variable a local variable -- not so easy, in fact, it is quite difficult. Change the name of the global variable. Find all the uses in all functions and replace it with a local variable or pass a parameter to the function. Much more complicated and time consuming.

        It isn't necessary the case globals should not be used. Defining the variables using the minimal scope required reduces the chance for bugs as shown above. It also reduces overall memory usage, usually, there are exceptions.

        As for passing the data or pointer to it to the USER data of the Window the reason for this is multiple copies of the same window being displayed. Imagine you have a Form that displays Person Details. Now you want to display two or more of these forms at the same time (Multiple word documents is another example). If you only have one set of globals and the form refers to the global names, you can't open a new Form and have it refer to a second data set to be displayed. Granted you could change the global to an array, but you have to pass the details of which index of the array to be displayed.

        Pass data to functions as paramters (or use the USER in a Window), don't have the function assume to know where the data comes from.


        Hope this helps...

        Comment


        • #5
          There is no problem in using Globals at all.

          Thats why they are there!

          The problem has to do more with two specific situations when using globals:

          (1) Poor naming of variables.
          Errors occur when you use variable names for globals which may be confused with locals. Because of this, I strongly recommend always using some kind of unique prefix for all global variable names.

          ie.

          GLOBAL gMyVariable&

          or

          GLOBAL GLB_MyVariable&

          I like to use in applications:

          GLOBAL App_MyVariable&

          When I see a variable with the prefix App_ in my code I immediately know it is a Global.

          By using a consistant prefix for all your global variable you will prevent many errors.

          (2) The need for instance data.
          Globals don't always work well for situations where one needs data storage per instance. For example when writing a custom window class, where you may have many instances of that window class, Globals may not work well. You need some way to track a specific instance of data storage. This is why the use of the extra window bytes (to store longs) is useful or even the use of window properties (I like to use atoms since they speed up the API property calls, rather than use strings).

          The point is that instance data is better suited to something other than globals.

          Now you can though combine the two.

          You could create an array of globals with the upper bount large enough to handle all the possible instances and then store the index into that array in the window extra bytes or a property.

          So there is nothing inherently wrong in using Globals. They just need to be used properly when they are best suited or in combination with other memory storage means when instance data is required.
          Chris Boss
          Computer Workshop
          Developer of "EZGUI"
          http://cwsof.com
          http://twitter.com/EZGUIProGuy

          Comment


          • #6
            I think the two big worries with globals are documentation (as mentioned above; that horse isn't getting up) and data integrity. It's possible that to sections of code could try to access a global at the same time. If both are doing read operations, it's not usually a problem. However, if one or both are write operations, now you have the potential for data corruption or ambiguity. Search the forums for ENTERCRITICALSECTON to see ways to prevent that hazard.

            I use globals in my programs fairly often. Usually for static data -- settings, login session data, etc. I've never had a problem, but I'm sure my apps are quite simple compared with some of the work doe by others on this board.
            Real programmers use a magnetized needle and a steady hand

            Comment


            • #7
              I have heard some extremists say you should never use globals. (much like the goto statement) However I find globals are useful and definitely have their place in some programs. To make them easy to spot and work with I start all their names with "g". In some ways they can be faster since they are only created once and don't need to be passed to each function. (trivial impact at best) Globals are also useful when dealing with some call back functions which only allow one parameter to be passed and you need several pieces of data in the function. (there are some tricks you can do with UDTs for this but you also need to deal with keeping them in scope while the callback runs... something that you don't have to worry about on globals)

              However one big drawback on using globals comes from maintaining the code. Lets say for example you want to reuse a function from that program in another program. If the function is using a global, it is a bigger pain to reuse the code because now you have to set them up in the other program also and remember how they are intermingled in the rest of the code to know how to use them. But on the other hand if you can pass all the variables to a given function using the parameters it is very easy to reuse the code in other programs because it is all self contained inside the function. This is the main reason I avoid globals when I can.
              "I haven't lost my mind... its backed up on tape... I think??" :D

              Comment


              • #8
                >there are some tricks you can do with UDTs for this but you also need to deal with keeping them in scope while the callback runs... something that you don't have to worry about on globals

                Never to happen, also the main reason (for novice) to use a global hWnd for the main window.
                Totally pointless.
                Need to see the first problem yet..
                hellobasic

                Comment


                • #9
                  oh gawd ... please don't tell me you use i as a global
                  -

                  Comment


                  • #10
                    John,

                    I have no doubt Globals are safe and useful to use, when used correctly.

                    For example, EZ... (you know, my GUI engine) software DLL has over 200 globals in it and a good number of global arrays. It has been used in a lot of software by my customers and proven reliable.

                    Globals are not somehow inherently bad as some may think.

                    If used only in the primary thread, then there is likely little need for syncronization.

                    Now if globals are shared across threads, then this is a different story. I use critical sections for this to prevent two threads (ie. one worker thread and the primary thread) from accessing a global variable at the same time.
                    Chris Boss
                    Computer Workshop
                    Developer of "EZGUI"
                    http://cwsof.com
                    http://twitter.com/EZGUIProGuy

                    Comment


                    • #11
                      I recently needed to work on some old pbcc code (converted from pdos).
                      This code had literally hundreds of globals.
                      Not only that there were several almost identical programs that needed updating.
                      As I said each was originally top down pbdos code that had been stuck into a PBCC PBMain().
                      Not a pretty picture.
                      What I did was turn each into a class with all the globals as instance variables.
                      Now I could combine all separate programs with no fear of global conflicts.

                      Another option is to place all your globals into a class specifically designed for your Application.
                      This should work great in multi-user projects. Have your globals and not worry about conflicts with others.

                      Code is for either PBWin9 or PbCC5

                      James

                      a trivial example:

                      Code:
                      '=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
                      'Using Classes as GLOBAL wrappers
                      '=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
                      ' Use PbWin9 or Pbcc5
                      #COMPILE EXE
                      #DIM ALL
                      '******************************************************************************
                      ' Property MACROS
                      '******************************************************************************
                      #IF NOT %DEF(%Prop_MACROS)
                          %Prop_MACROS = 1
                          MACRO PropGet(PropName,PropType)=PROPERTY GET PropName() AS PropType:PROPERTY=PropName:END PROPERTY
                          MACRO PropSet(PropName,PropType)=PROPERTY SET PropName(BYVAL param AS PropType):PropName=param:END PROPERTY
                          MACRO PropGetD(PropName,PropType,DispId)=PROPERTY GET PropName <DispId> () AS PropType:PROPERTY=PropName:END PROPERTY
                          MACRO PropSetD(PropName,PropType,DispId)=PROPERTY SET PropName <DispId> (BYVAL param AS PropType):PropName=param:END PROPERTY
                      #ENDIF
                      '******************************************************************************
                      DECLARE FUNCTION AllocConsole LIB "KERNEL32.DLL" ALIAS "AllocConsole" () AS LONG
                      DECLARE FUNCTION GetStdHandle LIB "KERNEL32.DLL" ALIAS "GetStdHandle" (BYVAL nStdHandle AS DWORD) AS DWORD
                      %STD_OUTPUT_HANDLE = -11&
                      %STD_INPUT_HANDLE  = -10&
                      '******************************************************************************
                      CLASS cMyGlobals
                          INSTANCE sStr1,sStr2 AS STRING
                          INSTANCE nVar1 AS SINGLE
                          INTERFACE iMyGlobals : INHERIT IUNKNOWN
                              PropGet(sStr1,STRING)
                              PropSet(sStr1,STRING)
                              PropGet(sStr2,STRING)
                              PropSet(sStr2,STRING)
                      
                              PropGet(nVar1,SINGLE)
                              PropSet(nVar1,SINGLE)
                          END INTERFACE
                      END CLASS
                      '==============================================================================
                      'GLOBALS
                      '******************************************************************************
                      GLOBAL oG1 AS iMyGlobals
                      GLOBAL oG2 AS iMyGlobals
                      '******************************************************************************
                      'Uses oG1 globals
                      '------------------------------------------------------------------------------
                      FUNCTION My1Func() AS LONG
                          CPRINT oG1.sStr1
                          CPRINT oG1.sStr2
                      END FUNCTION
                      '******************************************************************************
                      'Uses oG2 Globals
                      '==============================================================================
                      FUNCTION My2Func() AS LONG
                          CPRINT oG2.sStr1
                          CPRINT oG2.sStr2
                      END FUNCTION
                      '==============================================================================
                      FUNCTION PBMAIN()
                      
                          IF %DEF(%PB_WIN32) THEN
                              AllocConsole
                          END IF
                          oG1 = CLASS "cMyGlobals"
                          IF ISNOTHING(oG1) THEN
                              CPRINT "NO oG1"
                              CWAITFORENTER
                          END IF
                      
                          oG2 = CLASS "cMyGlobals"
                          IF ISNOTHING(oG2) THEN
                              CPRINT "NO oG2"
                              CWAITFORENTER
                          END IF
                          oG1.sStr1 = "ONE ONE"
                          oG1.sStr2 = "ONE TWO"
                          oG2.sStr1 = "TWO ONE"
                          oG2.sStr2 = "TWO TWO"
                      
                          My1Func
                          My2Func
                      
                          CPRINT "All is Well"
                          CWAITFORENTER
                      
                      END FUNCTION
                      '==============================================================================
                      '******************************************************************************
                      'CONSOLE PRINT ROUTINES
                      '******************************************************************************
                      SUB CPRINT (s AS STRING, OPT NoReturn AS LONG)
                          LOCAL h,n AS LONG
                      
                          h = GetStdHandle(%STD_OUTPUT_HANDLE)
                          n = FREEFILE
                      
                          OPEN HANDLE h FOR OUTPUT AS n
                      
                          IF ISMISSING(NoReturn) THEN
                              PRINT# n, s
                          ELSE
                              PRINT# n, s;
                          END IF
                      
                          CLOSE n
                      END SUB
                      '==============================================================================
                      SUB CWAITFORENTER ()
                          LOCAL h,n AS LONG, S AS STRING
                      
                          h = GetStdHandle(%STD_INPUT_HANDLE)
                      
                          n = FREEFILE
                          OPEN HANDLE h FOR INPUT AS n
                          LINE INPUT# n, S
                          CLOSE n
                      END SUB

                      Comment


                      • #12
                        Then tell us what they do, i mean 200!!
                        I am not sure but maybe 1 to 4 globals i use..
                        I really have to search.

                        In my tool i have this global structure but it serves a purpose, giving me information about the application.
                        hellobasic

                        Comment


                        • #13
                          My big problem is GLOBALs is code re-useability.

                          Existing, tested and debugged procedures cannot be simply #INCLUDEd or 'cut and pasted' into a different applications or even a new modules of the same application if they rely on GLOBAL variables.

                          Also, GLOBALs preclude use in re-entrant situations... e..g , upgrading an existing program to support multi-threaded operation or allowing a single window procedure to service multiple instances of a window, as in MDI applications.


                          MCM
                          Michael Mattias
                          Tal Systems (retired)
                          Port Washington WI USA
                          [email protected]
                          http://www.talsystems.com

                          Comment


                          • #14
                            Globals in DLL's are independent.
                            This allows easy threading and subclassing even from Visual Basic.

                            Code:
                            'Easy subclassing and threading even from Visual Basic
                            'Demonstrates globals are independent to each DLL
                            'Compile with %DLL=1
                            'Run with %DLL=0
                            #DIM ALL
                            %DLL = 1  '1 to make the DLL, 0 after making DLL to run
                            #IF %DLL = 0
                              #COMPILE EXE "main"
                              #INCLUDE "win32api.inc"
                              GLOBAL gCounter AS LONG
                            #ELSE
                              #COMPILE DLL "background.dll"
                              #INCLUDE "win32api.inc"
                              GLOBAL gCS AS CRITICAL_SECTION
                              GLOBAL gCounter AS LONG
                              GLOBAL gControl    AS LONG
                              GLOBAL gPrevWndProc AS LONG
                            #ENDIF
                            DECLARE FUNCTION MyDLL LIB "background.dll" AS LONG
                            #IF %DLL=0
                            FUNCTION PBMAIN () AS LONG
                              FOR gCounter = 1 TO 2
                                ? "The value of gCounter is" + STR$(gCounter) + " in main.exe"
                                ? "The value of gCounter is" + STR$(MyDLL) + " in background.dll"
                              NEXT
                            END FUNCTION
                            #ENDIF
                            #IF %DLL
                            SUB EnterCritical
                               EnterCriticalSection gCS
                            END SUB
                            '--------------------------------------------------------------
                            SUB LeaveCritical
                               LeaveCriticalSection gCS
                            END SUB
                            FUNCTION LIBMAIN(BYVAL hInstance AS DWORD, _
                                             BYVAL lReason AS LONG,    _
                                             BYVAL lReserved AS LONG) AS LONG
                              SELECT CASE AS LONG lReason
                                CASE %DLL_PROCESS_ATTACH
                                  '? "DLL attached"
                                  InitializeCriticalSection gCS
                                  LIBMAIN = 1
                                  EXIT FUNCTION
                                CASE %DLL_PROCESS_DETACH
                                  '? "This DLL is about to be unloaded
                                   DeleteCriticalSection gCS
                                      EXIT FUNCTION
                                CASE %DLL_THREAD_ATTACH
                                  '? "A [New] thread is starting (see THREADID)
                                  EXIT FUNCTION
                                CASE %DLL_THREAD_DETACH
                                  '? "This thread is closing (see THREADID)
                                  EXIT FUNCTION
                              END SELECT
                              LIBMAIN = 0
                              ? "Failure to initialize the DLL!
                            END FUNCTION
                            FUNCTION MyDLL EXPORT AS LONG
                              EnterCritical
                              gCounter = gCounter + 100
                              FUNCTION = gcounter
                              LeaveCritical
                            END FUNCTION
                            FUNCTION Peeker(Address AS DWORD, Characters AS LONG) EXPORT AS STRING
                              FUNCTION  = PEEK$(address???, characters)
                            END FUNCTION
                            SUB StartSubClass(h AS LONG) EXPORT
                              EnterCritical
                              gControl = h
                              gPrevWndProc = SetWindowLong(h, %GWL_WNDPROC, CODEPTR (pMyWindowProc))
                              LeaveCritical
                            END SUB
                            SUB EndSubClass() EXPORT
                              EnterCritical
                              'Form1.List1.Clear
                              'Form1.List1.AddItem "SetWindowLong using glPrevWndProc" + Str$(glPrevWndProc)
                              CALL SetWindowLong(gControl, %GWL_WNDPROC, gPrevWndProc)  'restore old address
                              gControl = 0
                              LeaveCritical
                            END SUB
                            FUNCTION pMyWindowProc(BYVAL gControl AS LONG, BYVAL uMsg AS LONG, _
                              BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
                              EnterCritical
                              IF uMsg = &H300 THEN
                                DIM s AS STRING
                                s = PEEKER(lParam, wParam)
                                EXIT FUNCTION
                              END IF
                              pMyWindowProc = CallWindowProc(gPrevWndProc, gControl, uMsg, wParam, lParam)
                              LeaveCritical
                            END FUNCTION
                            #ENDIF
                            How long is an idea?

                            Comment


                            • #15
                              Originally posted by Michael Mattias View Post
                              My big problem is GLOBALs is code re-useability.

                              Existing, tested and debugged procedures cannot be simply #INCLUDEd or 'cut and pasted' into a different applications or even a new modules of the same application if they rely on GLOBAL variables.

                              Also, GLOBALs preclude use in re-entrant situations... e..g , upgrading an existing program to support multi-threaded operation or allowing a single window procedure to service multiple instances of a window, as in MDI applications.


                              MCM
                              Just how far should we take it, Inc files might also contain functions/subs of the same name etc though the compiler is more likely to complain. My point was that using the User fields of a window is just another name for using a Global which is hard to document and can be dangerous.
                              Of course you can use Globals in re-entrant code without problems, can even be useful (like how deep am I in a recursive algorathim, do you put that in a window user field?). Yes if the function expects some variables to be initialsed then they should be local QED.
                              As for converting to multi-threaded there is absolutely no difference in the steps that must be taken in using a global or putting a pointer or value in a window user field just so you can say "look ma no globals"
                              I do appreciate the added complexity if a program is being developed by a team, thats why we first had DLL's and later COM to hopefully resolve DLL Hell but I doubt the majority of PB programmers are working in large group projects.
                              I know I shouldn't ask but just how many programmers do you have working on your product seeing this is always so important to you.

                              Comment


                              • #16
                                As I've learned more, I've used globals less and less. Still, most of the programs I write deal with the physical parameters of some external thing or device. Invariably there's a group of parameters (time, voltage, phase, impedance etc.) that almost every routine needs access to. It just makes sense to put everything related to that device into a UDT and have basically one global. I could do it with no globals at all, but the trade-off in robustness vs complexity doesn't seem worthwhile.

                                Best,
                                Conrad

                                Comment


                                • #17
                                  Of course you can use Globals in re-entrant code without problems
                                  That is not an accurate general statement at all. With care you can do it.

                                  , can even be useful (like how deep am I in a recursive algorathim...., do you put that in a window user field?).
                                  Recursion and reentrancy are two different things.

                                  With care you may use a GLOBAL variable to count recursion depth; but maybe it would be a little easier to just make any required recursion/depth counter an additional parameter to the recursing function and pass it by reference.

                                  In re-entrant situations (eg multi-threaded), the same procedure may be executing multiple times - asynchronously - which means you cannot be sure of the current value of any non-stack-based variable.

                                  And for the other part.... well, actually, yes, you DO put that in a window user field. Since you are always getting that value based on the stack-based 'hwnd' passed to the window procedure, it is totally re-entrant.

                                  MCM
                                  Last edited by Michael Mattias; 2 Aug 2009, 02:16 PM.
                                  Michael Mattias
                                  Tal Systems (retired)
                                  Port Washington WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                  • #18
                                    Originally posted by Michael Mattias View Post
                                    That is not an accurate general And for the other part.... well, actually, yes, you DO put that in a window user field. Since you are always getting that value based on the stack-based 'hwnd' passed to the window procedure, it is totally re-entrant.

                                    MCM
                                    Well I have always said I am not good at GUI, but I must have misunderstood a very basic of windows. It is my understanding that the handle of a window will never change unless I destroy it and recreate it. Why would I need to get it from the stack as a passed variable.
                                    The simple situation is simply you are happy to use Globals ie window or control user fields as long as not called Globals.
                                    Please explain clearly how using a window user field does not require the same sychronisation methods with threads as a Global.

                                    Comment


                                    • #19
                                      This subject would be better if we discussed real usages.
                                      Stabbing each other for a global..?

                                      I would like to argue on certain usages.
                                      For example the global hWndMain variable..?
                                      hellobasic

                                      Comment


                                      • #20
                                        would like to argue on certain usages.
                                        For example the global hWndMain variable..?
                                        Never necessary.

                                        Since "hwnd" (or "CB.HNDL") is always available in the window procedure - which is where ANY activity in a GUI program is initiated - 'hwnd' can be passed as a parameter to any procedure called therefrom.


                                        MCM
                                        Michael Mattias
                                        Tal Systems (retired)
                                        Port Washington WI USA
                                        [email protected]
                                        http://www.talsystems.com

                                        Comment

                                        Working...
                                        X