fhg
Volume 10, Number 10 -- March 17, 2010

A Good Use for Global Variables

Published: March 17, 2010

by Ted Holt

Using global data (fields, variables and constants) is generally considered to be poor programming practice. One reason is that a subroutine or subprocedure that uses global data is not easily placed into service elsewhere. Another reason is that undesirable side effects may occur when one routine changes a variable that another routine uses. However, in some situations global data makes sense. I would like to provide one example.

Assume a service program with several subprocedures. The source code for the module from which the service program is built looks like this:

H nomain

D/copy prototypes,mysvcpgm

P DoThing1        b                   export
D                 pi             
D  Parm1                        24a   varying const
... more code ...
P                 e

P DoThing2        b                   export
D                 pi             
D  Parm1                        12a   varying
D  Parm2                         9p 0 
D  Parm3                         5a  
... more code ...
P                 e

P DoThing3        b                   export
D                 pi             
D  Parm1                        24a
D  Parm2                        48a
... more code ...
P                 e

... more subprocedures ...

P DoThingN        b                   export
D                 pi             
D  Parm1                         4a
... more code ...
P                 e

The module consists of N (i.e., some number of) related subprocedures that work together to do something (e.g., pricing, costing, shipping, posting, etc.).

Suppose we wish to be able to control these routines in some way. For example, we wish to make them able to use either a 12- or 13-period accounting year. Or we want to make them produce printed output or not. How would we go about controlling the routines?

One way would be to add a parameter to each subprocedure. Let's say we want the subprocedures to produce a program dump when something goes wrong. Each subprocedure gets a new parameter. Let's call the new parameter ParmDump.

H nomain      

D/copy prototypes,mysvcpgm

P DoThing1        b                   export
D                 pi             
D  Parm1                        24a   varying const
D  ParmDump                       n   const
... more code ...
 /free
... more code ...
      monitor;
... more code ...
      on-error;
         if ParmDump;
            dump(a);
         endif;
      endmon;
... more code ...
 /end-free
P                 e

P DoThing2        b                   export
D                 pi             
D  Parm1                        12a   varying
D  Parm2                         9p 0 
D  Parm3                         5a  
D  ParmDump                       n   const
... more code ...
/free
... more code ...
      if SomethingWentWrong;
         if ParmDump;
            dump(a);
         endif;
      endif;
... more code ...
 /end-free
P                 e

P DoThing3        b                   export
D                 pi             
D  Parm1                        24a
D  Parm2                        48a
D  ParmDump                       n   const
... more code ...
/free
... more code ...
      if not %found(SomeFile);
         if ParmDump;
            dump(a);
         endif;
      endif;
... more code ...
 /end-free
P                 e

... more subprocedures ...

P DoThingN        b                   export
D                 pi             
D  Parm1                         4a
D  ParmDump                       n   const
... more code ...
/free
... more code ...
      if Balance < *zero and ParmDump;
         dump(a);
      endif;
... more code ...
 /end-free
P                 e

Each routine has been changed to produce a program dump if something goes wrong and ParmDump is on. Now we must change all the calls in the caller(s) to pass another parameter.

D  DumpOnError                    n
 /free
... more code ...
   if SomeCondition;
      DumpOnError = *on;
   Endif;
... more code ...
   DoThing1 (SomeData: DumpOnError);

The extra parameter in the call to DoThing1 is DumpOnError, which the caller must turn on in order to make the service program produce dumps. What sets SomeCondition? Whatever you want. I'll come back to that in a bit.

Here's another approach that's less burdensome. Rather than add a dump parameter to all N subprocedures, let's add a global variable to the service program to indicate whether errors are to cause dumps or not. Let's add code to each subprocedure to generate a dump when an error is found and DumpEnabled is on.

H nomain    

D/copy prototypes,mysvcpgm

D DumpEnabled     s               n   inz(*Off)

P DoThing1        b                   export
D                 pi             
D  Parm1                        24a   varying const
... more code ...
 /free
... more code ...
      monitor;
... more code ...
      on-error;
         if DumpEnabled;
            dump(a);
         endif;
      endmon;
... more code ...
 /end-free
P                 e
... etc. ...

Now let's add a subprocedure to control the global DumpEnabled variable.

P SetDump         b                   export
D                 pi
D  inSetDump                      n   const

 /free
     eval DumpEnabled = inSetDump;
 /end-free
P                 e

The global variable is DumpEnabled. Notice that it precedes the first subprocedure definition.

Now we modify the calling routine(s), like this:

/free
... more code ...
   if SomeCondition;
      SetDump (*on);
   Endif;
... more code ...
   DoThing1 (SomeData);

Now all subprocedures create dumps when an error arises.

There are plenty of ways to control the condition in the calling routine(s). For example, you might have calling programs check a certain position of a data area. Normally the data area would have a blank in that position, and dumps would not be produced. If the data area contains a D in that position, the caller executes SetDump with an argument of *ON. To control the condition, use the Change Data Area (CHGDTAARA) command.

CHGDTAARA DTAARA(SOMELIB/CONTROL (5 1)) VALUE('D')

In a situation where a group of routines needs common data, global data provides a simple and effective way to make sure that all subprocedures work together.




Copyright © 1996-2010 Guild Companies, Inc. All Rights Reserved.
Guild Companies, Inc., 50 Park Terrace East, Suite 8F, New York, NY 10034

Privacy Statement