The SCOPE_VARFETCH function returns variables outside the local scope of the currently running procedure or function. You can use it to examine or alter variables that are not otherwise visible, or to create new variables in other scopes.
In normal IDL operation, routines are not allowed to see variables outside their own scope. This restriction is a fundamental and important property of structured modular programming. Code that accesses non-local scope to alter variables within other active routines can quickly become extremely difficult to understand and maintain. For this reason, avoid using the IDL scope routines.
Despite this warning, there is an important class of application that requires the functionality provided by these routines. Programs, usually with graphical user interfaces, that import and export data from the callers’ scope need to be able to access the user’s data variables directly, and without requiring them to explicitly pass those variables to the application as parameters. The SCOPE_VARFETCH function is used to access those variables, and SCOPE_VARNAME is used to obtain the correct names with which to refer to them. SCOPE_LEVEL returns the scope level of the currently executing routine. The IDL iTools and the ENVI image processing software are examples of programs that need to be able to perform these operations.
Note: To use this routine successfully, you need to understand the information in Altering Accessed Variables and Avoiding Unwanted Copies.
When IDL starts, it is implicitly running within a main program named $MAIN$. You can call procedures or functions from $MAIN$, and those routines can in turn call other routines. Each such routine invocation is represented internally by IDL using an interpreter frame, which IDL uses to maintain access to all variables known to that invocation of that routine. These variables can be local to the routine, parameters passed into the routine by its caller, or global variables (such as common block variables or heap variables). The set of variables visible within a given interpreter frame forms the variable scope of the corresponding routine. As routines are called, IDL pushes interpreter frames onto a call stack, and as those routines return, their frames are removed from the call stack. In the case of a recursive routine (a routine that calls itself), each invocation of the routine is given its own distinct and unshared frame.
The frames on the interpreter stack are numbered starting at 1 and increasing by 1 for each routine. This number is the level of the frame and, by implication, the scope that the frame accesses. The call stack always contains the frame for $MAIN$, which is the routine in which IDL execution starts and the only routine in IDL from which you cannot return. $MAIN$ is therefore always level 1. Any routine called from $MAIN$ will be at level 2, routines called from there will be level 3, and so forth.
Returns the variable specified by VarName relative to the scope specified by the LEVEL keyword. The value returned by SCOPE_VARFETCH can be used by the caller to directly access, or alter, that variable.
If the COMMON keyword is set: If VarName is of non-string type, it is the positional index of the variable to be returned within the specified common block. If VarName is a string, it is the name of the variable to be returned within the common block. In this case, the name corresponds to the name used for that common block variable in the first routine that defined the common block.
Note: For more information, see COMMON Variable Names.
Set this keyword to a string containing the name of a common block to cause SCOPE_VARFETCH to return a variable contained within the specified block. The variable to return is determined by the value of VarName, as discussed above.
By default, SCOPE_VARFETCH will return only variables that already exist in the specified scope. Attempts to access a nonexistent variable will cause IDL to issue an error and halt execution of the program. Set this keyword to alter this behavior. If ENTER is set and the desired variable does not exist in the specified scope, SCOPE_VARFETCH will create a new variable within that scope and return the new variable. This keyword can be used to export data into other scopes. Generally, the scope of the calling routine and that of $MAIN$ are most likely to be useful destinations.
Set this keyword to an integer that specifies the scope within which the desired variable should be found. The level can be either an absolute value or relative to the current scope, as shown in the following table:
Level 0 always refers to the frame for the currently executing routine. This is the default value.
Negative levels are a relative specification and refer to the frames of active routines relative to the currently executing routine. Level -1 is the direct caller of the current routine, -2 is the caller of the caller of the current routine, and so forth. If you specify a level that is deeper than the current call stack, SCOPE_VARFETCH clips the value not to go past $MAIN$.
Positive levels are an absolute specification and refer directly to the specified frame. Level 1 is $MAIN$, level 2 is the routine called from $MAIN$, and so forth. If you specify a level that is past the currently executing routine, SCOPE_VARFETCH clips the value to the level of the current routine.
The LEVEL keyword cannot be used in conjunction with the COMMON keyword.
Set this keyword to cause SCOPE_VARFETCH to return the variable with the keyword name given by VarName from the _REF_EXTRA keywords within the specified level. Normally, a routine is not able to access the _REF_EXTRA keywords passed to it, and can pass them on only to other routines it calls. However, the SCOPE_VARFETCH REF_EXTRA keyword provides a mechanism by which a routine can examine its own _REF_EXTRA keywords.
Note: See Keyword Inheritance for more information.
The variable returned by SCOPE_VARFETCH can be used directly in IDL expressions to access the value of a variable from another scope. For example, the following statements access the value of a variable named mydata within the scope of the main program, $MAIN$:
PRINT, SCOPE_VARFETCH('mydata', LEVEL=1)
local_data = SCOPE_VARFETCH('mydata', LEVEL=1) + 100
Note that local_data contains only a copy of mydata, not a reference, so you can change local_data without affecting mydata.
If you do want to alter the value of a variable such as mydata, you must surround the call to SCOPE_VARFETCH with parentheses. For example, the following statements set mydata to a FINDGEN vector, and then increment it and add a constant to it. Finally, the value of the 5th element of the array is changed to 1000:
(SCOPE_VARFETCH('mydata', LEVEL=1)) = FINDGEN(100)
(SCOPE_VARFETCH('mydata', LEVEL=1)) += 57
(SCOPE_VARFETCH('mydata', LEVEL=1)) = 1000
To prevent IDL from making unwanted copies of the variable returned from SCOPE_VARFETCH, you should operate on the result of the SCOPE_VARFETCH function directly and not assign it to other variables.
For example, consider the following code, which prints the value of a variable mydata from the main program, $MAIN$:
temp = SCOPE_VARFETCH('mydata', LEVEL=1)
This code will produce the desired result, but at a higher than necessary cost. Assigning the variable returned by SCOPE_VARFETCH to the variable temp causes IDL to allocate new memory for temp and to make a copy of the mydata variable from $MAIN$ into temp. This process is slower, and will use twice as much memory as necessary, which can be a significant problem if the original mydata variable is very large. Instead, you should write the code this way:
PRINT, SCOPE_VARFETCH('mydata', LEVEL=1)
In cases where your code simply needs the value of a variable from another scope, assigning the result of SCOPE_VARFETCH to a local variable will use more memory and time, but is otherwise equivalent to using the direct result of the function. However, if your code wants to alter the original variable, using a local variable will produce the wrong answer. Consider the assignment code from above, modified to use a local variable:
temp = SCOPE_VARFETCH('mydata', LEVEL=1)
temp = FINDGEN(100)
temp += 57
temp = 1000
This code alters the value of the local variable temp, but the original variable mydata remains unaltered. The first line makes a copy of mydata, the second line discards that copy, and the remaining lines operate on the local variable.
SCOPE_VARFETCH allows transparent access to variables from other scopes as long as you call it in every place where the variable is needed. Any assignment of the result from SCOPE_VARFETCH to another variable will create a copy. Altering the value of this copy will not alter the original.
The following creates a variable in the main program scope, named newdata. It sets the value using the DIST function, and then uses SURFACE to display it:
(SCOPE_VARFETCH('newdata', /ENTER, LEVEL=1)) = DIST(128)
SURFACE, SCOPE_VARFETCH('newdata', LEVEL=1)
The following procedure will print the values of all keywords passed to it via the by-reference _REF_EXTRA keyword inheritance mechanism. In addition, it displays the name or names by which that variable is known in all currently active interpreter frames:
PRO scope_varfetch_ex, _REF_EXTRA=ref
FOR ridx = 0, N_ELEMENTS(ref)-1 DO BEGIN
kw = ref[ridx]
PRINT, 'Value of _REF_EXTRA keyword ', kw, ':'
HELP, SCOPE_VARFETCH( kw, /REF_EXTRA )
calls = SCOPE_TRACEBACK()
FOR i=N_ELEMENTS(calls)-1, 0, -1 DO BEGIN
name = SCOPE_VARNAME( SCOPE_VARFETCH(kw, /REF_EXTRA), $
cname = STREGEX( calls[i], '[^ ]*', /EXTRACT )
PRINT, FORMAT='(%" _REF_EXTRA %s name in level %s: %s")', $
kw, cname, name ? name : ''
Using this procedure, consider the output from the following IDL statements:
a = 1
b = 2
scope_varfetch_ex, k_a=a, k_b=b
Value of _REF_EXTRA keyword K_A:
<No name> INT = 1
_REF_EXTRA K_A name in level EXAMPLE:
_REF_EXTRA K_A name in level $MAIN$: A
Value of _REF_EXTRA keyword K_B:
<No name> INT = 2
_REF_EXTRA K_B name in level EXAMPLE:
_REF_EXTRA K_B name in level $MAIN$: B