namespace with variable variable - namespaces

I would create a namespace with variable names like :
proc show {} {
puts $fg::varName
puts $fg::x
}
set p "varName"
namespace eval fg {variable $p FirstValue}
namespace eval fg {variable x "Second variable"}
show
This works I think because it is outside a procedure. If I change the code:
proc show {} {
puts $fg::varName
puts $fg::x
}
# set p ""
proc xx {} {
# global p
set p "varName"
namespace eval fg {variable $p FirstValue}
namespace eval fg {variable x "Second variable"}
show
}
xx
I get an error i.e. can't read "p": no such variable.
It works if I uncomment
set p "" and
global p
but it seems a little cumbersome.

When you do:
namespace eval fg {variable $p FirstValue}
The code in brackets evaluated in the specified namespace (global namespace variables are also available there) and $p variable is local for the "xx" procedure.
As a workaround, you may pass the code "pre-evalueted". For example:
namespace eval fg [list variable $p FirstValue]

Related

How do you access a procedure's local variables in a namespace eval {} inside that same procedure?

I'm sure I'm just being stupid but would you please tell me how to get access to $name inside the namespace in order to set variable n to $name? I can only find how to do this when the procedure is in the namespace but not the other way 'round. No matter what I try, this errors stating no such variable name. Thank you.
proc getNS {name} {
namespace eval ns::$name {
variable n $name
}
}
This works but isn't really an answer unless the answer is simply that it cannot be done directly. Got it from this SO question. Bryan Oakley gave the answer but used [list set...] instead of [list variable...] and that will fail if there is a global variable of the same name. (It will modify the global rather than creating a new variable in the namespace.) It may have been different, of course, in 2009 when that answer was provided.
proc getNS {name} {
namespace eval ns::$name [list variable n $name]
namespace eval ns::$name {
variable a abc
}
}
set n xyz
getNS WEBS
chan puts stdout "ns n: $ns::WEBS::n; a $ns::WEBS::a, global n: $n"
# => ns n: WEBS; a: abc; global n: xyz
You can just use set with a fully qualified variable name that uses the desired namespace:
proc getNS {name} {
namespace eval ns::$name {} ;# Create namespace if it doesn't already exist
set ns::${name}::n $name
}
getNS foo
puts $ns::foo::n ;# foo
Another way is to use uplevel to refer to the scope of the proc that calls namespace eval:
proc getNS {name} {
namespace eval ns::$name {
set n [uplevel 1 {set name}]
}
}

Tcl: How to get namespace and procedures of calling namespace?

I have some generic procedure. I would like this procedure to be able to get the name of the namespace and names of the procedures within the namespace where this procedure is called.
I have tried following code:
proc register {} {
puts [info procs]
puts [namespace current]
}
namespace eval Foo {
proc bar {} {
puts bar
}
proc _baz {} {
puts baz
}
register
}
However, this prints results for the namespace where register is defined, not for the namespace where it is executed. It looks like there are no dedicated commands for these tasks or at least these are not info or namespace commands.
To get information about the calling context, use uplevel:
proc register {} {
puts [uplevel 1 [list info procs]]
puts [uplevel 1 [list namespace current]]
}

How to access a variable defined in proc 'a' from a different proc 'b' which does not call proc 'a'?

I am trying to execute a tcl script which makes exclusive calls to procs a and b. The two procs are not related to each other.
proc a {} {
set var1 "a"
}
proc b {} {
# Do something here with: $var1
}
# script.tcl
a
b
I do not have access to the script.tcl as well. When proc 'a' is called, I need to store the var1 somehow such that I can access it later within proc 'b' when it is called. How can I get the value of var1 in proc b? Doesn't seem like I can use 'global' and 'upvar' here?
A simple way is to define the variable in the global scope by preceding the variable name with ::
proc a {} {
set ::var1 "a"
}
proc b {} {
puts $::var1
}
Other methods would be to use the global command in each proc or to define the variable in a special namespace of its own.
Using variable instead of global offers a bit more flexibility if namespaces are involved
namespace eval n {
proc a {{value A}} {
variable var1
set var1 $value
return
}
proc b {} {
variable var1
puts "var1 is <$var1>"
}
}
n::a
n::b ;# => var1 is <A>
namespace eval n {a 42; b} ;# => var1 is <42>
puts $::var1 ;# => can't read "::var1": no such variable

how to declare a variable globally which is used only in proc

i am having a following code:
proc testList {setupFile ""} {
if {$setupFile == ""} {
set setupFile location
}
}
proc run {} {
puts "$setupFile"
}
I am getting syntax error. I know if i declare the setupFile variable outside the proc i.e in the main proc then i can append it with namespace say ::65WL::setupFile to make it global but not getting how to do that if a variable itself is defined in the proc only.
You can refer to the global namespace with ::.
proc testList {{local_setupFile location}} {
# the default value is set in the arguments list.
set ::setupFile $local_setupFile
}
proc run {} {
puts $::setupFile
}
Tcl variables that are not local to a specific procedure run need to be bound to a namespace; the namespace can be the global namespace (there's a special command for that) but doesn't need to be. Thus, to have a variable that is shared between two procedures, you need to give it an exposed name:
proc testList {{setup_file ""}} {
# Use the 'eq' operator; more efficient for string equality
if {$setup_file eq ""} {
set setup_file location
}
global setupFile
set setupFile $setup_file
}
proc run {} {
global setupFile
puts "$setupFile"
}
Now, that's for sharing a full variable. There are some other alternatives provided you only want to share a value. For example, these two possibilities:
proc testList {{setup_file ""}} {
if {$setup_file eq ""} {
set setup_file location
}
# Create a procedure body at run-time
proc run {} [concat [list set setupFile $setup_file] \; {
puts "$setupFile"
}]
}
proc testList {{setup_file ""}} {
if {$setup_file eq ""} {
set setup_file location
}
# Set the value through combined use of aliases and a lambda term
interp alias {} run {} apply {setupFile {
puts "$setupFile"
}} $setup_file
}
There are more options with Tcl 8.6, but that's still in beta.
you can use uplevel, upvar and/or global
proc testList {{setupFile ""}} {
if {$setupFile eq ""} {
set setupFile location;
uplevel set setupFile $setupFile;
}
}
proc run {} {
upvar setupFile setupFile;
puts "$setupFile";
}
or
proc run {} {
global setupFile;
puts "$setupFile";
}
the first is prefered.

Unable to pass a variable to a procedure using upvar in Tcl

I need a procedure that will be able to access, read and change a variable from the namespace of the caller. The variable is called _current_selection. I have tried to do it using upvar in several different ways, but nothing worked. (I've written small test proc just to test the upvar mechanism). Here are my attempts:
call to proc:
select_shape $this _current_selection
proc:
proc select_shape {main_gui var_name} {
upvar $var_name curr_sel
puts " previously changed: $curr_sel"
set curr_sel [$curr_sel + 1]
}
For my second attempt:
call to proc:
select_shape $this
proc:
proc select_shape {main_gui} {
upvar _current_selection curr_sel
puts " previously changed: $curr_sel"
set curr_sel [$curr_sel + 1]
}
In all the attempts, once it reaches this area in the code it says can't read "curr_sel": no such variable
What am I doing wrong?
EDIT:
The call for the function is made from a bind command:
$this/zinc bind current <Button-1> [list select_shape $this _current_selection]
at start I thought that it doesn't matter. but maybe It does.
I believe that bind commands operate in the global namespace, so that's where the variable is expected to be found. This might work:
$this/zinc bind current <Button-1> \
[list select_shape $this [namespace current]::_current_selection]
for upvar to work the variable must exist in the scope that you are calling it in. consider the following:
proc t {varName} {
upvar $varName var
puts $var
}
#set x 1
t x
If you run it as it is you'll get the error you are reporting, uncomment the set x 1 line and it will work.
In the example below I've tried to cover the most variants of changing variables from other namespace. It 100% works for me. Maybe it will help.
proc select_shape {main_gui var_name} {
upvar $var_name curr_sel
puts " previously changed: $curr_sel"
incr curr_sel
}
namespace eval N {
variable _current_selection 1
variable this "some_value"
proc testN1 {} {
variable _current_selection
variable this
select_shape $this _current_selection
puts " new: $_current_selection"
}
# using absolute namespace name
proc testN2 {} {
select_shape [set [namespace current]::this] [namespace current]::_current_selection
puts " new: [set [namespace current]::_current_selection]"
}
select_shape $this _current_selection
puts " new: $_current_selection"
}
N::testN1
N::testN2
#-------------------------------------
# Example with Itcl class
package require Itcl
itcl::class C {
private variable _current_selection 10
public method testC {} {
select_shape $this [itcl::scope _current_selection]
puts " new: $_current_selection"
}
}
set c [C #auto]
$c testC