ASB supports a series of extensions related to variable declarations, providing programmers more control over the scope and persistence of variables. This topic presents an overview of the general subject, followed by a detailed look at the related keywords.
Scope, the context within which a vairable can be referenced:
Global: Potentially visible from everywhere in the program. (Global/External Variables are not visible within functions and procedures unless ++pragma auto_extern declared within the routine.) This is the default for MAP variables except when declared inside a function or proc.
Local: Visible only within the function or procedure in which the variable was declared. This is the default for variables mapped within a function or procedure.
Module: Visible only within the current ++INCLUDE module (aka file), including within any functions or procedures contained therein. Declaration requires the PRIVATE keyword or ++PRAGMA PRIVATE_BEGIN and must appear outside of any functions or procedures.
Persistence, the duration of a variable's validity:
Static: Variable persists for duration of program. This is the default except for variables mapped within functions/procedures. Prefixing such MAP statements with STATIC will give them static duration. Unlike automatic variables (see below), there is only one copy of each static variable in memory, even in the case of recursion.
Automatic: Variable is automatically created and destroyed at runtime for each instance of a function or procedure. This is the default for variables mapped inside of a function or procedure. These are also called "stack" variables, because they are allocated on the stack. When the function or procedure returns, they get popped off the stack. Note that in the case of recursion, you can have multiple instances of a stack variable, one for each nesting level of the function call. Like all variables, they are always pre-initialized to zero or null unless the MAP statement contains an explicit initialization value.
Use of the keywords STATIC, PRIVATE, and PUBLIC in conjunction with MAP and DIMX statements to control the scope and persistence of variables:
MAP... Defines a global, static variable, unless it occurs within a function or procedure, in which case it defines a local automatic variable. Local variables, whether static or automatic, are invisible outside that routine. Note that if a MAP statement contains an explicit initialization value, then it has the effect of an assignment statement that gets executed at runtime. Thus, if MAP statement is in the flow of control in such a way that it can be executed multiple times, the definition of the variable only occurs once by the compiler but the optional initialization would occur each time the MAP statement was executed.
STATIC MAPn... Used only within functions and procedures to give the variable static persistence; the scope remains local. Note that if the MAP statement contains an initial value, it will only be assigned once. Warning: initialization of a static variable changes the actual RUN module in memory, which could create a logic problem if the module is inadvertently called (via indirect recursion) while it is still in use. For example, if ONE.SBX contains a static initialized variable, and ONE.SBX calls TWO.SBX, which in turn calls ONE.SBX again, that variable would not be re-initialized. Whether or not that is a problem may depend on whether TWO.SBX realizes that the call is recursive.
PRIVATE MAPn... Used only within a ++INCLUDE file, and outside of any function or procedure to define a variable with module scope and static persistence. This technique is useful to allow a group of related functions and procedures to share some common variables without having to expose them to the rest of the program. Note that like all MAP statements, they must be executed in order to achieve anything but null initialization. See ++PRAGMA PRIVATE_BEGIN for details on a technique to ensure proper initialization, and Global/External Variables for additional context regarding the visibility of variables within functions and procedures.
DIMX... Defines an array to be dynamically allocated at runtime. Note that the compiler defines the variable when the DIMX statement is encountered, as it would for a MAP statement, but the variable cannot be referenced at runtime until the DIMX statement is executed. The actual memory for the variable is allocated outside the user partition, and gets cleaned up when the program (or SBX) ends. If DIMX occurs within a function or procedure, by default the variable would be have local scope and persist only for the duration of the procedure or function. The memory allocation is freed automatically when the routine returns. You can add one of the keyword modifiers (STATIC, PRIVATE, PUBLIC) to change the scope and persistence. Note that DIMX is an extended implementation of the original DIM statement, which is deprecated and not officially supported within functions.
STATIC DIMX... STATIC DIMX is to DIMX as STATIC MAPn is to MAPn. Used only within functions or procedures to cause the DIMX allocation to persist across calls to the routine, while retaining local scope. To achieve both persistence and module scope, use PRIVATE DIMX instead. As a practical matter, to avoid executing the STATIC DIMX statement multiple times if the function is called multiple times, you will probably have to associate it with another local STATIC MAP flag variable that you set when the DIMX is executed.
PRIVATE DIMX... PRIVATE DIMX is to DIMX as PRIVATE MAPn is to MAPn—i.e. declares the array to have module scope and static persistence, except that a PRIVATE DIMX statement may occur anywhere in the module, including within a function or procedure. Because DIMX statements must be executed to initialize them before they are referenced (unlike MAP statements, there is no default null initialization for DIMX), module-scope DIMX arrays are typically initialized inside a function or procedure. Making sure such initialization occurs once and only once, before any use of the array, requires additional control logic, the failure of which can be a common source of runtime bugs. The ++PRAGMA PRIVATE_BEGIN feature provides an elegant solution for the initialization problem.
PUBLIC DIMX... This is a special form of DIMX that can occur only inside a function or procedure. Unlike PRIVATE DIMX, it doesn't have to be within a ++INCLUDE file. It overrides the normal default of local scope and stack persistence for variables defined within a function of procedure, instead making the variable visible anywhere in the entire program. This is handy in a situation where you want to use a function or procedure to create a dynamic memory structure, perhaps by loading the data from or across the internet, that is then available in the main program.
History
2013 February, A-Shell 6.1.1339 (Compiler edit 624): ++PRAGMA PRIVATE_BEGIN/END introduced
2011 October, A-Shell 6.0.1235: Fixes/refinements to PUBLIC DIMX. PRIVATE DIMX structures supported.
2011 May, A-Shell 6.0.1217: Implement automatic cleanup of local DIMX at end of function/procedure
2010 August, A-Shell 5.1.1190-1193: DIMX arrays now support reentrancy. STATIC DIMX fixes.
2009 March A,-Shell 5.1.1157 (Compiler edit 437): Important bug fixes to PRIVATE
2006 October, A-Shell 4.9.970: PRIVATE and PUBLIC keywords introduced