I cannot define variables in namespace (in TCL) which was defined previously in global scope. See my example:
xsct% $tcl_version
[8.5]
xsct% set foo 1
1
xsct% $foo
[1]
xsct% namespace eval ns {
set foo 2
set bar 3
}
3
xsct% $::ns::bar
[3]
xsct% $::ns::foo
can't read "::ns::foo": no such variable
xsct%
I have reproduced the issue online: http://tpcg.io/3SIBYG
How can I define variables in namespaces independently from global scope?
I use:
Win10
TCL 8.5 in Xilinx's XSCT TCL console
Always define variables in a namespace with the variable command at least the first time you access them, otherwise you end up with namespace variable resolution rules taking over and making your life unpleasant. They're weird (though actually very similar to how command resolution works) and you virtually never want them, and may get removed in Tcl 9. But until then, you're stuck doing:
namespace eval ns {
variable foo 2
variable bar 3
}
or:
namespace eval ns {
variable foo
set foo 2
variable bar
set bar 3
}
If you want to do arrays, you can. Do them like this (with only one argument to variable):
namespace eval ns {
variable ary
array set ary {foo 2 bar 3}
}
What the variable command actually does is make the variable in the namespace in unset state so that it still resolves when commands like set and array can find the variable and write to it.
Related
The following strained example, in attempt to match my real situation, completes successfully but not the way I'd like it to; nor do I understand why or can find an answer in one of my books.
I can see that proc func was called from the global level and, in turn, initially called proc cofunc; and, perhaps, that sets what upvar will use. But proc finalize calls the coroutine again and passes the variable name nbr to cofunc--which calls add. In this respect add "seems" to be two levels from finalize.
The upvar 2 $varName n in proc add is the variable name nbr and takes its value from the global variable of that name. I'd like to get it from proc finalize (so 15, not 85) which seems to be two levels away from proc add but apparently is not even in the list of caller contexts.
I assume [yield] is the reason; but would you please explain why and, more importantly, if the value in proc finalize was not a number but a "large" value that shouldn't be passed around, is it possible to upvar or uplevel from a coroutine?
Thank you for considering my question.
proc cofunc {callback vals} {
set var [yield]
$callback $vals $var
}
proc func {callback vals} {
set nbr 22
coroutine coro cofunc $callback $vals
}
proc add {vals varName} {
upvar 2 $varName n
chan puts stdout "varName: $varName; n: $n"
# => varName: nbr; n: 85
chan puts stdout [expr {[lindex $vals 0]+[lindex $vals 1]+$n}];
# => 115
}
proc finalize {nbr} {
coro nbr
}
set nbr 85
func add {10 20}
after 500 finalize 15
after 1000 set forever 1
vwait forever
The upvar (like the uplevel command) is defined in terms of the stack. The number argument says how many steps up the stack to take (or you can prefix the number with # to count down from the other end; that's a bit less common); it defaults to 1, the caller of the current procedure. There are two sorts of stack frame, one for procedures (and related entities, such as methods and lambda expressions) and another for direct namespaces (the global scope, namespace eval, and a few other things). All upvar does is go to the indicated stack frame, look up the named variable there, and make a local variable that is a link to it. By "link", I mean that the local variable is really a tagged C pointer to the implementation of the other variable. Once a linked variable is set up, access is very fast, as the slow part of access is resolving names to implementations, and local variables are usually compiled into indexed accesses.
The global and namespace upvar commands do something very similar, but with different lookup strategies. (Also, global assumes that the global and local names should match.) That's also part of what the variable command does.
In your code sample, the upvar 2 is to get past the cofunc procedure. I think it would be clearer if it was upvar #0, or if cofunc used tailcall $callback so a simple upvar could be used. And I'm pretty sure that the nbr accessed in add is not the same one as in func, as the latter's stack frame will be gone by then.
How to pass an argument by reference in the TCL procedure?
Look the following block of code:
proc test{&a} {
set a 7
}
set b 5
test b
puts $b
I waited that the output should be 7, but it was 5.
What is wrong with this code?
The trick is to use the upvar command to map the variable with that name in the caller's scope into the procedure's internal scope.
proc test {&a} {
# It's a good idea to use an explicit level of 1
upvar 1 ${&a} a
set a 7
}
I'd not normally name a variable &a, as they're annoying to use with $ syntax. I mostly prefer to use something like varName so that when someone reads the wrong-arguments error for test they get this readable version:
wrong # args: should be "test varName"
I'm trying to create some read-only variables to use with code evaluated in a safe interp. Using trace, I can generate an error on attempts to set them, but not when using unset:
% set foo bar
bar
% trace add variable foo {unset write} {apply {{var _ op} { error "$var $op trace triggered" }}}
% set foo bar
can't set "foo": foo write trace triggered
% unset foo
%
Indeed, I eventually noticed the documentation even says in passing:
Any errors in unset traces are ignored.
Playing around with different return codes, including custom numbers, they all seem to be ignored. It doesn't trigger an interp bgerror handler either. Is there any other way to raise an error for an attempt to unset a particular variable?
There really isn't. The key problem is that there are times when Tcl is going to unset a variable when that variable really is going to be deleted because its containing structure (a namespace, stack frame or object, and ultimately an interpreter) is also being deleted. The variable is doomed at that point and user code cannot prevent it (except by the horrible approach of never returning from the trace, of course, which infinitely postpones the death and puts everything in a weird state; don't do that). There's simply nowhere to resurrect the variable to. Command deletion traces have the same issue; they too can be firing because their storage is vanishing. (TclOO destructors are a bit more protected against this; they try to not lose errors — there's even pitching them into interp bgerror as a last resort — but still can in some edge cases.)
What's more, there's currently nothing in the API to allow an error message to bubble out of the process of deleting a namespace or call frame. I think that would be fixable (it would require changing some public APIs) but for good reasons I think the deletion would still have to happen, especially for stack frames. Additionally, I'm not sure what should happen when you delete a namespace containing two unset-traced variables whose traces both report errors. What should the error be? I really don't know. (I know that the end result has to be that the namespace is still gone, FWIW, but the details matter and I have no idea what they should be.)
I'm trying to create some read-only variables to use with code evaluated
Schelte and Donal have already offered timely and in-depth feedback. So what comes is meant as a humble addition. Now that one knows that there variables traces are executed after the fact, the below is how I use to mimick read-only (or rather keep-re_setting-to-a-one-time-value) variables using traces (note: as Donal explains, this does not extend to proc-local variables).
The below implementation allows for the following:
namespace eval ::ns2 {}
namespace eval ::ns1 {
readOnly foo 1
readOnly ::ns2::bar 2
readOnly ::faz 3
}
Inspired by variable, but only for one variable-value pair.
proc ::readOnly {var val} {
uplevel [list variable $var $val]
if {![string match "::*" $var]} {
set var [uplevel [list namespace which -variable $var]]
}
# only proceed iff namespace is not under deletion!
if {[namespace exists [namespace qualifiers $var]]} {
set readOnlyHandler {{var val _ _ op} {
if {[namespace exists [namespace qualifiers $var]]} {
if {$op eq "unset"} {
::readOnly $var $val
} else {
set $var $val
}
# optional: use stderr as err-signalling channel?
puts stderr [list $var is read-only]
}
}}
set handlerScript [list apply $readOnlyHandler $var $val]
set traces [trace info variable $var]
set varTrace [list {write unset} $handlerScript]
if {![llength $traces] || $varTrace ni $traces} {
trace add variable $var {*}$varTrace
}
}
}
Some notes:
This is meant to work only for global or otherwise namespaced variables, not for proc-local ones;
It wraps around variable;
[namespace exists ...]: These guards protect from operations when a given parent namespace is currently under deletion (namespace delete ::ns1, or child interp deletion);
In the unset case, the handler script re-adds the trace on the well re-created variable (otherwise, any subsequent write would not be caught anymore.);
[trace info variable ...]: Helps avoid adding redundant traces;
[namespace which -variable]: Makes sure to work on a fully-qualified variable name;
Some final remarks:
Ooo, maybe I can substitute the normal unset for a custom version and
do the checking in it instead of relying on trace
Certainly one option, but it does not give you coverage of the various (indirect) paths of unsetting a variable.
[...] in a safe interp.
You may want to interp alias between a variable in your safe interp to the above readOnly in the parent interp?
I want to know in which package all tcl commands will be available .
for example string , list etc .
When we want to use expect , we will use Expect package , similarly we are using these (string , list etc ...) commands without importing any package . i want to know in which package these are all belong ?
basically I came from java ... In java "java.lang" package default.
Formally, all commands that form the core of the Tcl language are provided by the Tcl package. That package is required for you before you run any code (and has to be; the package command itself is one of those commands). By historical convention, the large majority of Tcl commands are placed in the global namespace, which is on the command resolution path of all namespaces. (It's logically last on the path unless explicitly set with namespace path.) Some Tcl commands are defined in the ::tcl namespace, including a fair number of internal commands, direct access to which are not supported (if it isn't documented, it isn't supported; some are explicitly not supported but are relatively well known nonetheless; they're in the ::tcl::unsupported namespace). The namespace ::oo (and its children) are reserved for TclOO.
Commands defined by other packages — especially any newly-created packages — should be placed in a namespace with the same name as the package. This is merely a convention, and is not followed in older packages (particularly those that predate the namespace mechanism, such as Tk and TclX) because it is reckoned to be more important to maintain backward compatibility with existing scripts, but you will definitely find it easiest if you follow it. Also, it's usually the convention that the global namespace belongs principally to the user-defined application, despite the degree of pollution from other packages; it's yours to mangle as you see fit.
The exported commands of a namespace (which should typically be the public commands of the package with the same name) can be made available in another namespace without qualification via namespace import:
namespace eval ::foo {
proc grill {} { puts "grill!" }
namespace export grill
}
namespace eval ::bar {
namespace import ::foo::grill
grill
puts [namespace which grill]; # Prints ::bar::grill
puts [namespace origin grill]; # Prints ::foo::grill
}
Alternatively (from Tcl 8.5) you can update the resolution path of a namespace so it also looks in the other namespace:
namespace eval ::foo {
proc grill {} { puts "grill!" }
}
namespace eval ::bar {
namespace path ::foo
grill
puts [namespace which grill]; # Prints ::foo::grill
puts [namespace origin grill]; # Prints ::foo::grill
}
Note that the two mechanisms are a bit different: with imported commands, there is a local delegate for the command (and as that delegate is actually a command, you can rename it, etc.) whereas with path changes there are no such delegates; the command in the originating namespace is located directly during resolution of what grill means in that context.
List and strings does not come under any special packages. They are the basic commands. All the variables are treated as a string in tcl.
$ tclsh
% set l [list 1 2 3 4 5]
1 2 3 4 5
% lappend l 6
1 2 3 4 5 6
% set str "Hello, World"
Hello, World
I have two question about namespace in Tcl.
namespace eval ::dai {
set a 5
set b 10
namespace export *
}
My questions are:
export * - the export will make some variable inside this namespace can be used in other namespace, but what does this export * mean?
Set a 5, should not we use variable a 5? are they the same? some tutorials say inside namespace, we should use variable, what is the difference between variable and set in namespace?
1) As is (supposed to be) logical for Unix users, "*" means "everything available at the moment". It's like when you do rm -f * in a shell, the shell expands "*" and replaces it with a list of all the files present in the current directory. Actually, as namespace manual states you can specify more elaborate patters than simple "*". To learn what that "glob-style" thing mentioned there means read about string match.
2) The question "should not we use..." is incorrect because it depends on what you want to do. If you want to declare a variable located in the namespace, use variable. If you want to set a variable, use set, but bevare that if that variable x is not exist in the namespace yet, Tcl will attempt to find a global variablte with this name, see:
% set x 5
5
% namespace eval foo {
set x 10
}
10
% set x
10
# ^^ observe that the global variable has been assigned
% namespace eval foo {
variable x
set x 20
}
20
% set x
10
# ^^ observe that now `set x 20` found the variable `x` in the namespace and assigned to it
This is explained in the "NAME RESOLUTION" section of the namespace man page.
Note that this behaviour may appear illogical, but it actually matches that of the procedure scope: if you do set foo bar in a procedure body, this means setting the local variable unless you stated otherwise using either global or variable or by using a fully-qualified name (like ::ns::foo).
namespace export only applies to commands (i.e. procs) in the namespace: it registers them as being eligible to be imported into another namespace. For example:
% package require textutil
0.7.1
% textutil::splitx abcdefghij {[aeiou]}
{} bcd fgh j
% splitx abcdefghij {[aeiou]}
invalid command name "splitx"
while evaluating {splitx abcdefghij {[aeiou]}}
% namespace import textutil::*
% splitx abcdefghij {[aeiou]}
{} bcd fgh j