The uplevel command is similar to eval, except that it evaluates a command in a different scope than the current procedure. It is useful for defining new control structures entirely in Tcl. The syntax for uplevel is:
As with upvar, the level parameter is optional and defaults to 1, which means to execute the command in the scope of the calling procedure. The other common use of level is #0, which means to evaluate the command in the global scope. You can count up farther than one (e.g., 2 or 3), or count down from the global level (e.g., #1 or #2), but these cases rarely make sense.
When you specify the command argument, you must be aware of any substitutions that might be performed by the Tcl interpreter before uplevel is called. If you are entering the command directly, protect it with curly braces so that substitutions occur in the other scope. The following affects the variable x in the caller's scope:
uplevel {set x [expr $x + 1]}
However, the following will use the value of x in the current scope to define the value of x in the calling scope, which is probably not what was intended:
uplevel "set x [expr $x + 1]"
If you are constructing the command dynamically, again use list. This fragment is used later in Example 10-4:
uplevel [list foreach $args $valueList {break}]
It is common to have the command in a variable. This is the case when the command has been passed into your new control flow procedure as an argument. In this case, you should evaluate the command one level up. Put the level in explicitly to avoid cases where $cmd looks like a number!
uplevel 1 $cmd
Another common scenario is reading commands from users as part of an application. In this case, you should evaluate the command at the global scope. Example 16-2 on page 220 illustrates this use of uplevel:
uplevel #0 $cmd
If you are assembling a command from a few different lists, such as the args parameter, then you can use concat to form the command:
uplevel [concat $cmd $args]
The lists in $cmd and $args are concatenated into a single list, which is a valid Tcl command. Like eval, uplevel uses concat internally if it is given extra arguments, so you can leave out the explicit use of concat. The following commands are equivalent:
uplevel [concat $cmd $args] uplevel "$cmd $args" uplevel $cmd $args
Example 10-4 shows list assignment using the foreach trick described on Page 75. List assignment is useful if a command returns several values in a list. The lassign procedure assigns the list elements to several variables. The lassign procedure hides the foreach trick, but it must use the uplevel command so that the loop variables get assigned in the correct scope. The list command is used to construct the foreach command that is executed in the caller's scope. This is necessary so that $variables and $values get substituted before the command is evaluated in the other scope.
Example 10-5 illustrates a new control structure with the File_Process procedure that applies a callback to each line in a file. The call to uplevel allows the callback to be concatenated with the line to form the command. The list command is used to quote any special characters in line, so it appears as a single argument to the command.
proc File_Process {file callback} { set in [open $file] while {[gets $file line] >= 0} { uplevel 1 $callback [list $line] } close $in } |
What is the difference between these two commands?
uplevel 1 [list $callback $line] uplevel 1 $callback [list $line]
The first form limits callback to be the name of the command, while the second form allows callback to be a command prefix. Once again, what is the bug with this version?
uplevel 1 $callback $line
The arbitrary value of $line is concatenated to the callback command, and it is likely to be a malformed command when executed.
3.144.37.196