Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to use pass by reference in procs in TCL

2,260 views
Skip to first unread message

Swaroop

unread,
May 21, 2007, 3:06:07 AM5/21/07
to
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

sleb...@gmail.com

unread,
May 21, 2007, 3:37:40 AM5/21/07
to

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"

Gerald W. Lester

unread,
May 21, 2007, 8:38:12 AM5/21/07
to
sleb...@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|
+------------------------------------------------------------------------+

Jonathan Bromley

unread,
May 21, 2007, 9:51:29 AM5/21/07
to
On Mon, 21 May 2007 07:38:12 -0500, "Gerald W. Lester"
<Gerald...@cox.net> wrote:

>sleb...@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...@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.

Fredderic

unread,
May 23, 2007, 1:04:17 AM5/23/07
to
On Mon, 21 May 2007 15:51:29 +0200,
Jonathan Bromley <jonathan...@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

Uwe Klein

unread,
May 23, 2007, 2:34:06 AM5/23/07
to
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

suchenwi

unread,
May 23, 2007, 3:38:47 AM5/23/07
to
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".

Bruce Hartweg

unread,
May 23, 2007, 9:22:54 AM5/23/07
to

I think you meant *upvar*

bruce

Fredderic

unread,
May 23, 2007, 11:59:49 AM5/23/07
to

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

Fredderic

unread,
May 23, 2007, 12:02:21 PM5/23/07
to
On Wed, 23 May 2007 08:22:54 -0500,
Bruce Hartweg <doNO...@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

Darren New

unread,
May 23, 2007, 12:40:43 PM5/23/07
to
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.

Bruce Hartweg

unread,
May 23, 2007, 1:22:59 PM5/23/07
to

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

Bruce Hartweg

unread,
May 23, 2007, 1:24:38 PM5/23/07
to
Fredderic wrote:
> On Wed, 23 May 2007 08:22:54 -0500,
> Bruce Hartweg <doNO...@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

Fredderic

unread,
May 25, 2007, 1:31:44 PM5/25/07
to
On Wed, 23 May 2007 12:22:59 -0500,
Bruce Hartweg <doNO...@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

Fredderic

unread,
May 25, 2007, 1:35:12 PM5/25/07
to
On Wed, 23 May 2007 12:24:38 -0500,
Bruce Hartweg <doNO...@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

Bruce Hartweg

unread,
May 25, 2007, 2:10:29 PM5/25/07
to

Overall it sounds as the real problem is a horrible design mess ;)

Bruce

Bruce Hartweg

unread,
May 25, 2007, 2:13:20 PM5/25/07
to
Fredderic wrote:
> On Wed, 23 May 2007 12:24:38 -0500,
> Bruce Hartweg <doNO...@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

Fredderic

unread,
Jun 3, 2007, 11:29:14 PM6/3/07
to
On Fri, 25 May 2007 13:13:20 -0500,
Bruce Hartweg <doNO...@nowhere.com> wrote:

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

Gerald W. Lester

unread,
Jun 3, 2007, 11:35:47 PM6/3/07
to

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.

miguel sofer

unread,
Jun 4, 2007, 7:13:12 AM6/4/07
to
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
...
}


0 new messages