This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

'static' vars, upvar and uplevel

Help for those learning Tcl or writing their own scripts.
Post Reply
User avatar
incith
Master
Posts: 275
Joined: Sat Apr 23, 2005 2:16 am
Location: Canada

'static' vars, upvar and uplevel

Post by incith »

This post is meant for those that can explain this crap to me. :)

I wanted to make use of static variables in a script recently, that is, vars that will not get reset once the script is re-ran, etc. Basically I went and searched, discovered static vars do not exist in Tcl, and then found this code: http://phaseit.net/claird/comp.lang.tcl ... tml#static

Code: Select all

    proc static {args} {
      # set procName [lindex [info level [expr [info level]-1]] 0]
      set procName "incith::test"
      # putlog "procName is (${procName})\n"
      foreach varName $args {
        uplevel 1 "upvar #0 {$procName:$varName} $varName"
      }
    }
A quick usage example of this would be:

Code: Select all

namespace eval incith {
  namespace eval test {
    proc myProc {} {
      static count
      if {![info exists "incith::test:count"]} {
        set "incith::test:count" 1
      }
      incr "incith::test:count"
      putlog "count is now ${count}\n"
    }
  }
}
Declaring the 'static count' variable pushes the count var into the incith::test namespace and as such will not be forgotten.

As you can see I modified the set procName, which I'll now explain. When I first put it into a test script, the test script was under the same namespace above, namespace eval incith { namespace eval test {, and $procName became incith::test::PROC_NAME:var, where PROC_NAME was the calling procedure, e.g. incith::test::myProc:count based on the above example. Then I went and added proc static into my XRL script and procName would not fetch the namespace it was in, but instead it would only get the procedure name, and e.g. store the var as "myProc:count", this was baffling to me, which is why I just ended up forcing set procName to my namespaces.

The questions in all of this, is this really necessary? Is there some extra work being done? The point is just to not forget variables basically, so that when the proc is re-run it knows what a certain variable it's using was since the last call, and so on. It works, at least, but I can't help but feel that it's just plain wrong. It's also dang annoying to be typing out 60 char variable names with the namespaces etc. Discuss!
m
metroid
Owner
Posts: 771
Joined: Wed Jun 16, 2004 2:46 am

Post by metroid »

Why don't you just use variable?

AFAIK, variable isn't reset after a rehash.

Code: Select all

namespace eval somens {
    variable moo 1
}
User avatar
incith
Master
Posts: 275
Joined: Sat Apr 23, 2005 2:16 am
Location: Canada

Post by incith »

Well, all my scripts use namespaces and variables already. With this you don't have to setup variables for use later on at the beginning..

This works well for quick things, not that variables may or may not.

Other applicable uses for static variables might include something like:

Code: Select all

# never send the same result twice, barebones example
static last_result
while {$::last_result eq $cur_result} {
  cur_result = [fetch_result]
}
set "::last_result" $cur_result
User avatar
user
 
Posts: 1452
Joined: Tue Mar 18, 2003 9:58 pm
Location: Norway

Post by user »

I'm not sure I get what you want. Do you want the variable to be saved, to keep its value even if the interpreter is restarted or are you just looking for a way to use global variables inside a namespace?
Have you ever read "The Manual"?
User avatar
incith
Master
Posts: 275
Joined: Sat Apr 23, 2005 2:16 am
Location: Canada

Post by incith »

If you did something like..

Code: Select all

proc myProc {} {
  set last_result ""
  # fails already, last_result is not going to be remembered
}
The static proc does basically push variables into the namespace. It seems useful to me, but I do not understand upvar and uplevel so I'm not sure how it works, or it's the best solution.
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

Upvar accesses variables in the proc that called the current one (or global if this is the first level of call). Uplevel executes a piece of tcl-code in the same level as above. Using the level argument with either allows you to move several levels upward in the function call stack.

A quick demonstration of upvar:

Code: Select all

proc sub1 {} {
 set myvar "Foo"
 puts stdout "sub1: myvar is $myvar"
 sub2
 puts stdout "sub1: myvar is $myvar"
}
proc sub2 {} {
 upvar myvar thevar
 if {[catch {puts stdout "sub2: myvar is $myvar"} error]} {
  puts stdout "sub2: Could not read local \$myvar: $error"
 }
 puts stdout "sub2: thevar is $thevar"
 set thevar "Bar"
 puts stdout "sub2: Setting thevar to $thevar"
}
sub1
sub1: myvar is Foo
sub2: Could not read local $myvar: can't read "myvar": no such variable
sub2: thevar is Foo
sub2: Setting thevar to Bar
sub1: myvar is Bar
Here I use upvar in sub2 in order to access, and modify, the value of "myvar" which only exists in sub1. This is possible since sub2 was called by sub1, and moving one step upwards in the function call stack would place us in sub1, giving us access to the variables here. As sub2 exits, the link between sub1->myvar and sub2->thevar is broken while sub1->myvar retains its value. As sub1 exits, it's variablespace is wiped and myvar is lost.
NML_375
User avatar
user
 
Posts: 1452
Joined: Tue Mar 18, 2003 9:58 pm
Location: Norway

upvar

Post by user »

You can also give upvar an absolute level number (#0 for the global scope)

Code: Select all

upvar #0 globalvarName localvarName
...or a fully qualified variable name (making the level irrelevant)

Code: Select all

upvar ::ns1::ns2::ns3::namespaceVar localVar
If you create a proc that gets the name of the variable to import from an external source, make sure you specify a level, or you could get in trouble (if the variable name is an integer)

Code: Select all

proc myset {var val} {
	upvar 1 $var Var; set Var $val
}
Have you ever read "The Manual"?
Post Reply