upvar - Create link to variable in a different stack frame http://purl.org/tcl/home/man/tcl8.4/TclCmd/upvar.htm . '''upvar''' ''?level? otherVar myVar ?otherVar myVar ...?'' This command arranges for one or more local variables in the current procedure to refer to variables in an enclosing procedure call or to global variables. ''Level'' may have any of the forms permitted for the '''[uplevel]''' command, and may be omitted if the first letter of the first ''otherVar'' isn't # or a digit (it defaults to 1). For each ''otherVar'' argument, '''upvar''' makes the variable by that name in the procedure frame given by ''level'' (or at global level, if level is '''#0''') accessible in the current procedure by the name given in the corresponding ''myVar'' argument. The variable named by ''otherVar'' need not exist at the time of the call; it will be created the first time ''myVar'' is referenced, just like an ordinary variable. There must not exist a variable by the name ''myVar'' at the time '''upvar''' is invoked. ''MyVar'' is always treated as the name of a variable, not an array element. Even if the name looks like an array element, such as '''a(b)''', a regular variable is created. ''OtherVar'' may refer to a scalar variable, an array, or an array element. '''Upvar''' returns an empty string. The '''upvar''' command simplifies the implementation of call-by-name procedure calling and also makes it easier to build new control constructs as Tcl procedures. `upvar` makes ''myVar'' an alias for ''otherVar''. ''otherVar'' can ---- [CMcC]: Could someone write about what happens if otherVar is a namespace-qualified name? Does a fully-qualified name work? What does a partially-qualified name do? [Lars H]: Nothing special, much; these variables are also looked up according to the normal rules. Fully qualified names are straightforward. Partially qualified names are relative to the namespace of the specified ''level'', but the tendency of partially qualified names to bind to existing variables that are relative to the :: namespace can produce surprising effects that may obscure this. ---- From http://www.psg.com/~joem/tcl/faq.html I see: ** Using `[upvar]` with an array ** Use '''upvar''' rather than try to use '''[global]''' variables when possible. If the function is event driven, you are forced to use global variables. ====== # print elements of an array proc show_array arrayName { upvar $arrayName myArray foreach element [array names myArray] { foreach element [array names myArray] { puts stdout "${arrayName}($element) = $myArray($element)" } } proc main {} { set arval(0) zero set arval(1) one show_array arval ====== To return an array from a procedures, just take the array name in as an argument, as above. Any changes you make in the array will be made in the parent's array as well. Extended Tcl introduces a concept called keyed lists which are arrays made out of lists of key-value pairs and can be passed by value to routines, over networks, etc. ---- [RS] [upvar] can also be used for both global and local variables: upvar #0 foo foo ;# equivalent to: global foo upvar 0 foo bar ;# assigns alias 'bar' to a local variable 'foo' In the example above, any changes made to elements in `$myArray` will be ---- A recent exchange on the [AOLserver] mailing list resulted in a page with guidelines for using upvar and [uplevel] being published at http://dqd.com/~mayoff/notes/tcl/upvar.html . ** `upvar` prefix* for Variables in an [Array] ** ---- Some of the information at the mentioned site seems to be incorrect. Especially in reference to uplevel. I didn't look at in great detail however. Does this advice simply reflect best practice or what can be done? ====== ---- # Purpose - # Utility to turn an subset of an array into alias upvar'd variables for local function use. # This assumes the array is defined in one level above where this routine is called. # Todo - # allow array to be defined at other levels. # why does it not work unless I quote ${arName}(ind) with {} ?? # # Right now the source array has to be in the global level # proc upAr { arName index } { upvar $arName ar foreach ind [array names ar ${index}*] { regsub $index $ind {} indVar if { [info level ] == 1 } { # This was executed from level 0 uplevel 1 [list upvar #0 ${arName}($ind) $indVar] } else { uplevel 1 [list upvar #0 ${arName}($ind) $indVar] } } } [Art M.] ---- [AMG] I like that ''level'' is optional, but I don't like the method used to identify whether or not it is present. The following code (an in-place lsort): ====== proc naive_iplsort {lst} { set var [lsort $var] } } breaks whenever `$lst` starts with `#` or a numeral, because `[upvar]` guesses breaks whenever $lst starts with # or a numeral, since [[upvar]] guesses that its first parameter is ''level'' rather than ''otherVar''. ======none % set data {4 3 2 1 0}; set #0 {9 8 7 6 5}; set 0a {e d c b a} % naive_iplsort data 0 1 2 3 4 % naive_iplsort #0 wrong # args: should be "upvar ?level? otherVar localVar ?otherVar localVar ...?" % naive_iplsort 0a expected integer but got "0a" The fix is to explicitly supply ''level'': The fix is explicitly supply ''level'': ====== proc paranoid_iplsort {lst} { set var [lsort $var] } } This makes it abundantly clear to Tcl that `$lst` is an not ''level'' but an ''otherVar'' argument, but I think it's kind of painful to type, since it admits that Tcl has the feature that ''level'' is optional, but that feature can '''never''' be used safely. At least it works: This makes it abundantly clear to Tcl that $lst is a variable name and not a level. But I think it's kind of painful to type, since it admits that Tcl has a feature (''level'' is optional) that can '''never''' be used safely. At least it works: ======none % set data {4 3 2 1 0}; set #0 {9 8 7 6 5}; set 0a {e d c b a} % paranoid_iplsort data 0 1 2 3 4 % paranoid_iplsort #0 5 6 7 8 9 % paranoid_iplsort 0a a b c d e I know it may be a bit too late to change this, but I'd prefer that `[upvar]` I know it may be a bit too late to change this, but I'd prefer that [[upvar]] use a different algorithm to determine whether its first argument is ''level'' or ''otherVar''. I'll explain by way of demonstration: ====== rename upvar Upvar proc upvar {args} { # Received zero arguments. # Received zero arguments. # Allow [upvar] to raise an error. # Received an even number of arguments. # Received an even number of arguments. # Conclude that the first is a variable name. # Prepend $args with the default level, 1. set args [linsert $args 0 1] # Received an odd number of arguments. # Received an odd number of arguments. # Conclude that the first is a level number. # Let the real [upvar] do its thing. uplevel 1 Upvar $args } } And a test: ====== % upvar 0 0a alpha % set 0a success; puts $alpha success % proc test {} {upvar 0a beta; puts $beta} % test success This should also work in the case of multiple variable pairs. Ironically, I use `[uplevel] 1` in the above code. `[uplevel]` has a similar Ironically, I use [[uplevel 1]] in the above code. [[[uplevel]]] has a similar problem--- "Level cannot be defaulted if the first command argument starts with a digit or #." But there's no possibility of a cutesy workaround wrapper because it can legally accept ''any'' number of arguments, whereas ''upvar'' can only take an even number (not counting ''level''). So perhaps for the sake of consistency with `[uplevel]` I should just go ahead So perhaps for the sake of consistency with [[uplevel]] I should just go ahead and put those blasted 1's in there anyway. :^) ---- [[ [Tcl syntax help] | [Arts and Crafts of Tcl-Tk Programming] ]] [[ [Category Command] | [Category Introspection] | [Category Control Structure] ]]