|
|
 |
 |
 |
 |
TCL(Tool Command Language) Scripting
|
 |
 |
 |
 |
 |
 |
 |
 |
How to use pass by reference in procs in TCL
Hi, I need to write a script in which it uses proc's. I want to know how to use pass by reference to the proc's. A small example would be great. -Swaroop
On May 21, 3:06 pm, Swaroop <swaroop.t@gmail.com> wrote: > Hi, > I need to write a script in which it uses proc's. I want to know > how to use pass by reference to the proc's. A small example would be > great.
Tcl doesn't support pass by "reference", everything (from the programmer's perspective) tcl passes are plain strings. But we can get similar functionality by passing by "name" (think about how [incr] works as opposed to how [lindex] works): # Example of passing a variable by name. # Simple reset proc. If it's a number zero it # if not make it an empty string: proc reset {varname} { # We use upvar to gain access to the variable: upvar 1 $varname var if {[string is double -strict $var]} { set var 0 } else { set var "" } } # Calling the proc: set x 100 set y "Hello" puts "$x, $y" reset x reset y puts "$x, $y"
slebet @yahoo.com wrote: > On May 21, 3:06 pm, Swaroop <swaroop.t @gmail.com> wrote: >> Hi, >> I need to write a script in which it uses proc's. I want to know >> how to use pass by reference to the proc's. A small example would be >> great. > Tcl doesn't support pass by "reference", everything (from the > programmer's perspective) tcl passes are plain strings. But we can get > similar functionality by passing by "name" (think about how [incr] > works as opposed to how [lindex] works):
Ah, in formal Computer Science terms upvar implements "pass by reference" semantics (in Tcl the name of a variable is its reference) and uplevel implements "pass by name" semantics. While most languages do not implement "pass by name", there are several languages which do -- Tcl being one of them and Algol tbeing he "first" (to my knowledge). -- +--------------------------------+---------------------------------------+ | Gerald W. Lester | |"The man who fights for his ideals is the man who is alive." - Cervantes| +------------------------------------------------------------------------+
On Mon, 21 May 2007 07:38:12 -0500, "Gerald W. Lester"
<Gerald.Les @cox.net> wrote: >slebet @yahoo.com wrote: >> On May 21, 3:06 pm, Swaroop <swaroop.t @gmail.com> wrote: >>> Hi, >>> I need to write a script in which it uses proc's. I want to know >>> how to use pass by reference to the proc's. A small example would be >>> great. >> Tcl doesn't support pass by "reference", everything (from the >> programmer's perspective) tcl passes are plain strings. But we can get >> similar functionality by passing by "name" (think about how [incr] >> works as opposed to how [lindex] works): >Ah, in formal Computer Science terms upvar implements "pass by reference" >semantics (in Tcl the name of a variable is its reference) and uplevel >implements "pass by name" semantics.
Although it might be fairer to say that upvar is "take by reference" and uplevel is "take by name", since in both cases it's a run-time choice by the callee. Algol-60 subprograms have no choice in the matter at runtime... -- Jonathan Bromley, Consultant DOULOS - Developing Design Know-how VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK jonathan.brom@MYCOMPANY.com http://www.MYCOMPANY.com The contents of this message may contain personal views which are not the views of Doulos Ltd., unless specifically stated.
On Mon, 21 May 2007 15:51:29 +0200, Jonathan Bromley <jonathan.brom @MYCOMPANY.com> wrote: >>> Tcl doesn't support pass by "reference", everything (from the >>> programmer's perspective) tcl passes are plain strings. But we can >>> get similar functionality by passing by "name" (think about how >>> [incr] works as opposed to how [lindex] works): >> Ah, in formal Computer Science terms upvar implements "pass by >> reference" semantics (in Tcl the name of a variable is its >> reference) and uplevel implements "pass by name" semantics. > Although it might be fairer to say that upvar is "take by reference" > and uplevel is "take by name", since in both cases it's a run-time > choice by the callee. Algol-60 subprograms have no choice in the > matter at runtime... A problem I've come to face on a few occasions, is how to "name" a procedure local variable. In particular, how to set a variable write trace on a procedure local variable, that consults the value stored in another local variable. Fredderic
Fredderic wrote: > A problem I've come to face on a few occasions, is how to "name" a > procedure local variable. In particular, how to set a variable write > trace on a procedure local variable, that consults the value stored in > another local variable.
My caffeine level isn't up to this yet. could you perchance give an (simple please) example? uwe
On 23 Mai, 07:04, Fredderic <put_my_name_h@optusnet.com.au> wrote: > A problem I've come to face on a few occasions, is how to "name" a > procedure local variable. In particular, how to set a variable write > trace on a procedure local variable, that consults the value stored in > another local variable.
You mean alias one local variable to another? That's just uplevel 0 this that so that every access to "that" will be routed to "this".
suchenwi wrote: > On 23 Mai, 07:04, Fredderic <put_my_name_h @optusnet.com.au> wrote: >> A problem I've come to face on a few occasions, is how to "name" a >> procedure local variable. In particular, how to set a variable write >> trace on a procedure local variable, that consults the value stored in >> another local variable. > You mean alias one local variable to another? That's just > uplevel 0 this that > so that every access to "that" will be routed to "this".
I think you meant *upvar* bruce
On Wed, 23 May 2007 08:34:06 +0200, Uwe Klein <uwe_klein_habertw @t-online.de> wrote: >> A problem I've come to face on a few occasions, is how to "name" a >> procedure local variable. In particular, how to set a variable >> write trace on a procedure local variable, that consults the value >> stored in another local variable. > My caffeine level isn't up to this yet. > could you perchance give an (simple please) example? The real crux of it, is that with global or namespace variables, you can pass them by name without any problems. When can quite easily (if a little tediously) fully-qualify the name of a namespace variable before passing it to a function, and even easier in the case of globals. That name can then kick around for as long as you want, being passed through several functions which need not even know it's there, until at some random point in the following call stack it finally gets pulled out and handed to upvar, and being fully qualified, the "level" specified with the upvar call generally doesn't make a lick of difference. But to do that with a local variable, you have to take special care to either; 1) remember the absolute call depth of the source "local" variable 2) count the relative call stack depth as you call other proc's or 3) temporarily create a unique global or namespace to hold the value All three of those options are messy, at best. I've found two partial solutions to the issue... 1) have an [upref] proc that returns something like {upvar #n varname} which can be readily [eval]d to obtain the variable, or passed to an [upget] proc that will return the value of the referenced variable. 2) have a proc that copies the variable contents to a global or namespace variable for the life of the call, and returns the fully-qualified name of the global variable. I did this by having a global counter, and an array; I'd create an array entry with the next counter value, initialise it to the value of the local variable, and use a write trace to write changes back to the variable, and an unset trace on the original variable to remove it when it's no longer needed. Fix #1, though keeping well with the pass-by-name philosophy, requires un-natural handling of the "reference". Fix #2 has the unfortunate side effect that every write to the shadow variable is then copied by a second write to the original variable. This I think is down right ugly, especially if there's the possibility that the variable might be modified several times, though it allows the usual "natural" handling of references. It is also possible to set up a push-pull mechanism by setting a write trace that will unset itself, and set a read trace on the complimentary variable to "pull" the value over and reset the system. But I think it ends up being less efficient unless you do in fact expect the shadow variable to be updated multiple times. Fredderic
On Wed, 23 May 2007 08:22:54 -0500, Bruce Hartweg <doNOT @nowhere.com> wrote: > > You mean alias one local variable to another? That's just > > uplevel 0 this that > > so that every access to "that" will be routed to "this". > I think you meant *upvar* It also falls down when you need a temporary alias of a local variable, in a non-local scope. The only way I can think of to unset an upvar alias, is to create it in its own namespace, and destroy the namespace when you're done with it. But I've got the feeling that the machinery behind namespaces is a little bit more involved than a the machinery used to create a simple upvar alias. Fredderic
Fredderic wrote: > The only way I can think of to unset an upvar alias, is to create it in > its own namespace, and destroy the namespace when you're done with it.
Which also leads to memory leaks when for some reason you bomb out in an event handler unexpectedly and bgerror can't tell what it's safe to clean up and what isn't. -- Darren New / San Diego, CA, USA (PST) His kernel fu is strong. He studied at the Shao Linux Temple.
Fredderic wrote: > On Wed, 23 May 2007 08:34:06 +0200, > Uwe Klein <uwe_klein_habertw @t-online.de> wrote: >>> A problem I've come to face on a few occasions, is how to "name" a >>> procedure local variable. In particular, how to set a variable >>> write trace on a procedure local variable, that consults the value >>> stored in another local variable. >> My caffeine level isn't up to this yet. >> could you perchance give an (simple please) example? > The real crux of it, is that with global or namespace variables, you > can pass them by name without any problems. When can quite > easily (if a little tediously) fully-qualify the name of a namespace > variable before passing it to a function, and even easier in the case > of globals. That name can then kick around for as long as you want, > being passed through several functions which need not even know it's > there, until at some random point in the following call stack it > finally gets pulled out and handed to upvar, and being fully qualified, > the "level" specified with the upvar call generally doesn't make a lick > of difference. > But to do that with a local variable, you have to take special care to > either; > 1) remember the absolute call depth of the source "local" variable > 2) count the relative call stack depth as you call other proc's > or > 3) temporarily create a unique global or namespace to hold the value
or 4 - use upvar on each call, even if you don't use the variable directly. proc one {name} { upvar $name x set a 3 two x }
proc two {name} { upvar $name y three y }
proc three {name} { upvar $name z set z [expr {$z*$z}] }
proc test {} { set myvar 2 one myvar two myvar three myvar puts "myvar = $myvar" }
so by the time you get to three it is always referencing the local variable myvar in the test proc, but doesn't care if that is 1 2 or 3 levels away, and there are no extra copies or global/namespace polluting
Fredderic wrote: > On Wed, 23 May 2007 08:22:54 -0500, > Bruce Hartweg <doNOT @nowhere.com> wrote: >>> You mean alias one local variable to another? That's just >>> uplevel 0 this that >>> so that every access to "that" will be routed to "this". >> I think you meant *upvar* > It also falls down when you need a temporary alias of a local variable, > in a non-local scope.
I'm not sure what you mean here, care to give an example? bruce
On Wed, 23 May 2007 12:22:59 -0500, Bruce Hartweg <doNOT @nowhere.com> wrote: >> But to do that with a local variable, you have to take special care >> to either; >> 1) remember the absolute call depth of the source "local" variable >> 2) count the relative call stack depth as you call other proc's >> 3) temporarily create a unique global or namespace to hold the >> value > or 4 - use upvar on each call, even if you don't use the variable > directly. Okay... Now I remember the use case I was initially trying to allude to... Function [f1] calls function [do_something] which does something useful. Depending on the outcome, [do_something] calls one of two (or possibly more) other functions passed to it as arguments, originally, to produce some desired output. These other functions require extra arguments, which [do_something] doesn't need to know about; it just gets handed a couple lists, one of which it uses as the command to a [catch] statement. Then after handling any error that may have been returned, it itself returns the result of the [catch] as its own return value. [do_something] is expecting (and expected to in turn return), a boolean success code. Now, this piece of old code is needed to run in an environment it wasn't designed to function in; for the most part, that was achieved with a very simple wrapper function. But now, instead of producing some output and finishing, it has to update some state to reflect what output needs to be produced a little further on. The existing functions were fairly easily massaged to produce the right output as a state update, but they don't have (nor need to have) all the information required. The rest is appended to their "output" by the wrapper which called [do_something], before the value is stored in the proper place. To add a little more joy to the mix, variable traces are used to trigger the next part of the process as soon as the real state variables are updated, so it's necessary to either pass far more information than needed to each of the handler functions, just so they can essentially pass it back again (which also means they're no longer suited to other parts of the program from where they're being called), or, store the temporary state update from the handler functions in a temporary variable, combine that with the extra information, and then update the appropriate state variable from the [do_something] wrapper function. The definition of [do_something] is actually in a separate package, so re-implementing it really isn't on the TODO list. It works perfectly well as-is. So, in short, I use your option 4 already in a few places, but it's not applicable here because one of the intermediary functions has no understanding of the argument through which this variable name is being passed, and therefore has no means to do the necessary [upvar] and thus maintain the chain. Fredderic
On Wed, 23 May 2007 12:24:38 -0500, Bruce Hartweg <doNOT @nowhere.com> wrote: > Fredderic wrote: >>>> You mean alias one local variable to another? That's just >>>> uplevel 0 this that >>>> so that every access to "that" will be routed to "this". >>> I think you meant *upvar* >> It also falls down when you need a temporary alias of a local >> variable, in a non-local scope. > I'm not sure what you mean here, care to give an example? proc someproc {some args} { ... do some stuff ... upvar 0 localvar ::somespace::nonlocal ... do some more stuff ... }
Now, how to get rid of ::somespace::nonlocal, since we don't need it anymore. It should disappear on its own when [someproc] ends, but before then? Fredderic
Fredderic wrote: > On Wed, 23 May 2007 12:22:59 -0500, > Bruce Hartweg <doNOT @nowhere.com> wrote: >>> But to do that with a local variable, you have to take special care >>> to either; >>> 1) remember the absolute call depth of the source "local" variable >>> 2) count the relative call stack depth as you call other proc's >>> 3) temporarily create a unique global or namespace to hold the >>> value >> or 4 - use upvar on each call, even if you don't use the variable >> directly. > Okay... Now I remember the use case I was initially trying to allude > to... > Function [f1] calls function [do_something] which does something useful. > Depending on the outcome, [do_something] calls one of two (or possibly > more) other functions passed to it as arguments, originally, to produce > some desired output. These other functions require extra arguments, > which [do_something] doesn't need to know about; it just gets handed a > couple lists, one of which it uses as the command to a [catch] > statement. Then after handling any error that may have been returned, > it itself returns the result of the [catch] as its own return value. > [do_something] is expecting (and expected to in turn return), a boolean > success code. > Now, this piece of old code is needed to run in an environment it > wasn't designed to function in; for the most part, that was achieved > with a very simple wrapper function. But now, instead of producing > some output and finishing, it has to update some state to reflect what > output needs to be produced a little further on. The existing > functions were fairly easily massaged to produce the right output as > a state update, but they don't have (nor need to have) all the > information required. The rest is appended to their "output" by the > wrapper which called [do_something], before the value is stored in the > proper place. > To add a little more joy to the mix, variable traces are used to trigger > the next part of the process as soon as the real state variables are > updated, so it's necessary to either pass far more information than > needed to each of the handler functions, just so they can essentially > pass it back again (which also means they're no longer suited to > other parts of the program from where they're being called), or, store > the temporary state update from the handler functions in a temporary > variable, combine that with the extra information, and then update the > appropriate state variable from the [do_something] wrapper function. > The definition of [do_something] is actually in a separate package, so > re-implementing it really isn't on the TODO list. It works perfectly > well as-is. > So, in short, I use your option 4 already in a few places, but it's not > applicable here because one of the intermediary functions has no > understanding of the argument through which this variable name is being > passed, and therefore has no means to do the necessary [upvar] and thus > maintain the chain. > Fredderic
Overall it sounds as the real problem is a horrible design mess ;) Bruce
Fredderic wrote: > On Wed, 23 May 2007 12:24:38 -0500, > Bruce Hartweg <doNOT @nowhere.com> wrote: >> Fredderic wrote: >>>>> You mean alias one local variable to another? That's just >>>>> uplevel 0 this that >>>>> so that every access to "that" will be routed to "this". >>>> I think you meant *upvar* >>> It also falls down when you need a temporary alias of a local >>> variable, in a non-local scope. >> I'm not sure what you mean here, care to give an example? > proc someproc {some args} { > ... do some stuff ... > upvar 0 localvar ::somespace::nonlocal > ... do some more stuff ... > } > Now, how to get rid of ::somespace::nonlocal, since we don't need it > anymore.
what do you mean get rid of it? > It should disappear on its own when [someproc] ends, but before then?
why? ::somespace::nonlocal is a completely external entity that has nothing to do with someproc, all the upvar does is create a local alias to that spot, the spot itself is independant. I still am not quite sure of what the problem is. Bruce
On Fri, 25 May 2007 13:13:20 -0500, Bruce Hartweg <doNOT @nowhere.com> wrote: > Fredderic wrote: >> proc someproc {some args} { >> ... do some stuff ... >> upvar 0 localvar ::somespace::nonlocal >> ... do some more stuff ... >> } >> Now, how to get rid of ::somespace::nonlocal, since we don't need it >> anymore. > what do you mean get rid of it? >> It should disappear on its own when [someproc] ends, but before >> then? > why? ::somespace::nonlocal is a completely external entity that > has nothing to do with someproc, all the upvar does is create > a local alias to that spot, the spot itself is independant. I know what [upvar] does. What I want to know, is how to undo it. Simple, really. Once you create that local alias, how do you uncreate that local alias. Get rid of it totally, all traces of its existence, gone. Zip. Zilch. Nothing left. If I re-create the original variable, it won't be accessible by the alias name, or anything else for that matter. As to why, for the purpose of which I raised the question in the first place, probably in the range of 10-20 of the things would be created per "request". Requests would "normally" come in two or three times a minute, with bursts of about every 2-3 seconds for 10-30 minutes, and every 4-5 seconds another hour or two, a day. The application typically runs for a month which, except when I'm playing with kernels, is my usual reboot interval. Sum it all up, and that's a fair few dead local aliases just dangling around pointing off to variables that don't exist anymore. Probably not enough to be a significant resource drain, but it's still too much for comfort. So, after using [upvar] to create a local (or in this case non-local) alias, how do I uncreate it? Why doesn't really matter, since alternatives have already been applied, but for the sake of the original question and anyone else finding themselves in a similarly sticky spot, the question still remains; how do you undo an [upvar]? Fredderic
Fredderic wrote: > On Fri, 25 May 2007 13:13:20 -0500, > Bruce Hartweg <doNOT @nowhere.com> wrote: >> Fredderic wrote: >>> proc someproc {some args} { >>> ... do some stuff ... >>> upvar 0 localvar ::somespace::nonlocal >>> ... do some more stuff ... >>> } >>> Now, how to get rid of ::somespace::nonlocal, since we don't need it >>> anymore. >> what do you mean get rid of it? >>> It should disappear on its own when [someproc] ends, but before >>> then? >> why? ::somespace::nonlocal is a completely external entity that >> has nothing to do with someproc, all the upvar does is create >> a local alias to that spot, the spot itself is independant. > I know what [upvar] does. What I want to know, is how to undo it. > Simple, really. Once you create that local alias, how do you uncreate > that local alias. Get rid of it totally, all traces of its existence, > gone. Zip. Zilch. Nothing left. If I re-create the original > variable, it won't be accessible by the alias name, or anything else > for that matter. > As to why, for the purpose of which I raised the question in the > first place, probably in the range of 10-20 of the things would be > created per "request". Requests would "normally" come in two or three > times a minute, with bursts of about every 2-3 seconds for 10-30 > minutes, and every 4-5 seconds another hour or two, a day. The > application typically runs for a month which, except when I'm playing > with kernels, is my usual reboot interval. > Sum it all up, and that's a fair few dead local aliases just dangling > around pointing off to variables that don't exist anymore. Probably > not enough to be a significant resource drain, but it's still too much > for comfort. > So, after using [upvar] to create a local (or in this case non-local) > alias, how do I uncreate it? Why doesn't really matter, since > alternatives have already been applied, but for the sake of the > original question and anyone else finding themselves in a similarly > sticky spot, the question still remains; how do you undo an [upvar]?
First off you do not undo an upvar. That being said, It would seem to me that you for what you want to do, you upvar to a "unique" namespace then delete the namespace when done. -- +--------------------------------+---------------------------------------+ | Gerald W. Lester | |"The man who fights for his ideals is the man who is alive." - Cervantes| +------------------------------------------------------------------------+
On Jun 4, 12:29 am, Fredderic <put_my_name_h@optusnet.com.au> wrote: > On Fri, 25 May 2007 13:13:20 -0500, > Bruce Hartweg <doNOT@nowhere.com> wrote: > > Fredderic wrote: > >> proc someproc {some args} { > >> ... do some stuff ... > >> upvar 0 localvar ::somespace::nonlocal > >> ... do some more stuff ... > >> } > >> Now, how to get rid of ::somespace::nonlocal, since we don't need it > >> anymore.
a) "Sorry Dave, I can't do that". Just try it: % proc a {} {upvar 0 x ::y; b} % a bad variable name "::y": upvar won't create namespace variable that refers to procedure variable The core refuses to create that variable, as it would leave a namespace variable ::y pointing to (essentially) free memory after the proc returns. All the resources allocated to a proc call should be freed on return, and ::y would fail that condition. b) you cannot delete a variable created by upvar, except when its environment dies (proc return for locals, namespace deletion otherwise). What you can do is upvar it somewhere else. Note that this is just an explanation of the way things are; if you think it can be improved, let us please discuss a concrete proposal - here, in the wiki, or submit a TIP. Note that I tend to agree with Bruce anyway: this is a messy design. This being said, maybe your solution involves passing down the result of a timely call to [info level], to let the called procs know which vars they have to link to? Or maybe store the level itself in some global variable? Not enough details to have an opinion ... The "pass down the level" option would be something like: proc f1 {...} { set level #[info level] ... set arglist [list somefunc $level foo bar] set res [do_something aba bab $arglist] ... } proc do_something {x y cmd} { ... eval $cmd ... } proc somefunc {level a b} { upvar $level thatname thisname ... }
|
 |
 |
 |
 |
|