Tuesday, June 12, 2012

TCL - Variable scope - global and upvar


Tcl evaluates variables within a scope delineated by procs, namespaces (see Building reusable libraries - packages and namespaces), and at the topmost level, the global scope.
The scope in which a variable will be evaluated can be changed with the global and upvar commands.
The global command will cause a variable in a local scope (inside a procedure) to refer to the global variable of that name.
The upvar command is similar. It "ties" the name of a variable in the current scope to a variable in a different scope. This is commonly used to simulate pass-by-reference to procs.
You might also encounter the variable command in others' Tcl code. It is part of the namespace system and is discussed in detail in that chapter.
Normally, Tcl uses a type of "garbage collection" called reference counting in order to automatically clean up variables when they are not used anymore, such as when they go "out of scope" at the end of a procedure, so that you don't have to keep track of them yourself. It is also possible to explicitly unset them with the aptly named unsetcommand.
The syntax for upvar is:
  • upvar ?level? otherVar1 myVar1 ?otherVar2 myVar2? ... ?otherVarN myVarN?
The upvar command causes myVar1 to become a reference to otherVar1, and myVar2 to become a reference to otherVar2, etc. The otherVar variable is declared to be at level relative to the current procedure. By default level is 1, the next level up.
If a number is used for the level, then level references that many levels up the stack from the current level.
If the level number is preceded by a # symbol, then it references that many levels down from the global scope. Iflevel is #0, then the reference is to a variable at the global level.
If you are using upvar with anything except #0 or 1, you are most likely asking for trouble, unless you really know what you're doing.
You should avoid using global variables if possible. If you have a lot of globals, you should reconsider the design of your program.
Note that since there is only one global space it is surprisingly easy to have name conflicts if you are importing other peoples code and aren't careful. It is recommended that you start global variables with an identifiable prefix to help avoid unexpected conflicts.

Example

proc SetPositive {variable value } {
upvar $variable myvar
set myvar [expr {
if {$value < 0} { -$value}] } else {
} return $myvar
set myvar $valu e } SetPositive x 5 SetPositive y -5
{ upvar 1 $y z
puts "X : $x Y: $y\n" proc two {y} ;# tie the calling value to variable z
two levels up to a puts "two: Z: $z A: $a" ;# Output the val
upvar 2 x a ;# Tie variable x ues, just to confirm set z 1 ;# Set z, the passed variable to 1;
$y z ;# This ties the calling value to varia
set a 2 ;# Set x, two layers up to 2; } proc one {y} { upvar ble z puts "one: Z: $z" ;# Output that value, to check it is 5
one y ;# Call one, and output X and Y after the c
two z ;# call proc two, which will change the value }all. puts "\nX: $x Y: $y" proc existence {variable} { upvar $variable testVar if { [info exists testVar] } { puts "$variable Exists"
tting a simp
} else { puts "$variable Does Not Exist" } } set x 1 set y 2 for {set i 0} {$i < 5} {incr i} { set a($i) $i; } puts "\ntesting uns ele variable" # Confirm that x exists. existence x # Unset x unset x puts "x has been unset" # Confirm that x no longer exists.
existence x

No comments:

Post a Comment

Popular Posts