I have a question about upvar command in TCL. Using upvar command, we have have a reference to a global variable or a local variable in other procedure. I saw the following code:
proc tamp {name1 name2} {
upvar $name1 Ronalod
upvar $name2 Dom
set $Dom "Dom"
}
this procedure is called as tamp name1 name2 , and there is no global variables name1, name2 defined outside of it, how this upvar works in this case?
When you call
upvar 1 $foo bar, it finds the variable in the caller’s scope whose name is in thefoovariable, and makes the localbarvariable into an alias for it. If the variable doesn’t exist, it is created in an unset state (i.e., the variable record exists but it has no value. In fact, the implementation uses aNULLto represent that information, which is why Tcl has noNULLequivalent;NULLindicates non-existence) but the link is still created. (It only gets torn down when the local scope is destroyed or ifupvaris used to point the local variable at something else.)So let’s look at what your code is really doing:
The first line says that we’re creating a command called
tampas a procedure, that that procedure will have two mandatory formal arguments, and that those arguments are calledname1andname2.The second line says that we’re binding a variable name in the caller (the
1level indicator from my earlier explanation is optional, but strongly advised in idiomatic code) that is given by thename1variable (i.e., the first argument to the procedure) to the local variableRonalod. Henceforth, all accesses to that local variable (until the end of the stack frame’s life) will be actually performed on the bound variable in the caller.The third line is pretty much the same, except with
name2(second argument) andDom(local variable).The fourth line is actually pretty funky. It reads the
Domvariable to get a variable name (i.e., the variable named in the second argument to the procedure call) and sets that named variable to the valueDom. Remember, in Tcl you use$to read from a variable, not to talk about the variable.The result of the procedure call will be the result of the last command in its body (i.e., the literal
Domsincesetyields the contents of the variable as its result, a value it has just assigned). (The last line is wholly uninteresting as it is just the end of the procedure body.)The net result of calling this command is actually going to be pretty much nothing at all unless the second argument names a variable containing either
RonalodorDom. That’s pretty confusing. Of course, the confusing bit is really that funkysetwith a variable first argument. (That’s almost always a bad idea; it’s an indicator of Bad Code Smell.) Had you used this instead, things would have been simpler:In this case, the variable that
Domis coupled to (i.e., the one named by the second argument to the procedure) would be set toDom; the variables would in effect be being passed-by-reference. That extra$makes a huge difference!