Consider a library structured like this:
package provide ::mylib 1.0
namespace eval ::mylib {
namespace export f
proc f { action } {
# pass action around
g $action
}
proc g { action } {
eval $action
}
}
If I try to use it like this, it won't work:
....
namespace eval ::user {
set x 10
::mylib::f { puts $x }
}
The reason is that $x is not known in mylib. I can fix it like this:
namespace eval ::user {
set x 10
::mylib::f { puts $::user::x }
}
This works, but qualifying each variable in the argument to ::mylib::f is awkward. Another possibility is to wrap the code inside another namespace eval:
namespace eval ::user {
set x 10
::mylib::f { namespace eval { puts $x } }
}
Better, but still ugly. If it were Ruby or Perl, I would simply pass a closure to ::mylib::f. What is the best practice in Tcl?
BTW, I'm currently on Tcl 8.3, but hope to have the possibility to upgrade to a newer version soon, so solution in terms of 8.3 AND more recent versions are wellcome.
tl;dr version: namespace code
The namespace code command is intended for this situation. You pass it the script that you want to be encapsulated, and it hands you back a version that has magic in it so that it handles being called from anywhere. Here's how you might use it.
namespace eval ::user {
variable x 10
::mylib::f [namespace code { puts $x }]
}
Internally, it uses namespace inscope to do this. You're recommended to not use that directly; namespace code is the convenient way of doing this.
Note that it even works if the callback site passes arguments to it, so long as that site is doing:
eval $callback [list "the argument is this"]
(In fact, in 8.3 the eval isn't necessary because of a gross hack in unknown, but please do it the way I recommend above because we took that hack out in later versions. It was pretty awful.)
Related
I have a series of Tcl scripts which are being executed in an interpreter (Cadence Innovus). I want a way to group commands and execute them with a single call. Functionally I can achieve this by writing each group of commands in a separate file and calling source <group_file_name>. However it is inconvenient to define so many files.
I have tried to do this with:
proc {} {
commands...
}
This doesn't give me the functionality I need though. I believe it executes the commands in a lower namespace and the variables that are set do not remain.
Is there a way to get this functionality (a single file with callable functions), but that execute in the same namespace?
Well, you can always use uplevel or namespace eval inside that procedure to change the current namespace. If you use uplevel, you would do this:
proc foo {} {
uplevel 1 {
TheFirstCommand
TheSecondCommand
TheThirdCommand
}
}
With namespace eval, you might instead do this:
proc foo {} {
namespace eval ::theTargetNamespace {
TheFirstCommand
TheSecondCommand
TheThirdCommand
}
}
Things get a bit more complicated if you're wanting to use local variables with the scope-changers, but the principle for how to manage things is about the same whichever mechanism you use.
proc foo {} {
set a [uplevel 1 { TheFirstCommand }]
set b [uplevel 1 { TheSecondCommand }]
# Dynamically-generate the script to run; it's trivial code generation
uplevel 1 [list TheThirdCommand $a $b]
}
Switching to namespace eval is pretty much a drop-in replacement.
let us say that I have a namespace. I want to find all of the namespace procs.
For example,
let us say that I have this code:
namespace eval foo {
proc me {} { puts "yey!" }
proc moo {} { puts "ney!" }
}
puts [ getAllNamespaceProcs foo ] ; # getAllNamespaceProcs is what I do not know how to do
This should yield:
> { me moo }
Thanks.
The info procs command can be told what namespace to look in:
info procs ::foo::*
This will yield ::foo::me ::foo::moo (or the other way round; lsort it if you care) and you can filter that (e.g., with lmap and namespace tail) if you want to get the result that you mention.
I have a Tcl code that is being sourced from a C application using:
Tcl_Eval(tcl_interp, "source nmsp.tcl")
Everything runs fine.
However, the namespace scope isn't preserved. For example, the following file:
#!/bin/sh
# namespace evaluation
namespace eval bob {
namespace eval joe {
proc proc1 {} {}
}
proc proc2 {} {
puts "proc2"
}
proc ::proc3 {} {
puts "proc3"
}
proc joe::proc4 {} {
puts "proc4"
}
}
puts "Namespace calling [info procs ::bob\::*]"
when run by itself will produce this output:
Namespace calling ::bob::proc2
But when sourcing from Tcl_Eval will not print anything. In fact, the proc2 procedure can be called by itself fine without any namespace designation.
Anyone knows what may be causing it? I really like the encapsulation that namespaces provide.
Seems fine to me.
I created the following Tcl extension to perform your Tcl_Eval:
#include <tcl.h>
static int
DotestCmd(ClientData clientData, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[])
{
return Tcl_Eval(interp, "source test_namespace.tcl");
}
int DLLEXPORT
Testnamespace_Init(Tcl_Interp *interp)
{
if (Tcl_InitStubs(interp, "8.4", 0) == NULL) {
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, "dotest", DotestCmd, NULL, NULL);
return Tcl_PkgProvide(interp, "testnamespace", "1.0");
}
Being on windows, I compiled this using:
cl -nologo -W3 -O2 -MD -DNDEBUG -DUSE_TCL_STUBS -I\opt\tcl\include -c test_namespace.c
link -dll -release -out:testnamespace.dll test_namespace.obj \opt\tcl\lib\tclstub85.lib
and then created a test_namespace.tcl file with the content you posted above. Running this produces the following:
C:\opt\tcl\src>tclsh
% load testnamespace.dll Testnamespace
% dotest
Namespace calling ::bob::proc2
%
and further introspection shows things are as I would expect from that script:
% namespace children ::
::platform ::activestate ::bob ::tcl
% namespace children ::bob
::bob::joe
%
You probably are doing something weird in your C code first if this is really not working for you.
Update
The above example is for extending tcl with a compiled package. Apparently the OP is embedding Tcl into some other application. A trivial example of doing this is provided here which also runs the same command to the same effect as above. In reality when embedding Tcl into an application the code should use the tclAppInit.c file and provide its own Tcl_AppInit function. By running the usual Tcl_Main you get the full capabilities for processing events (needed for fileevents or after commands) and the interactive shell. An example of that follows the trivial version:
/* trivial embedding Tcl example */
#include <tcl.h>
#include <locale.h>
int
main(int argc, char *argv[])
{
Tcl_Interp *interp = NULL;
int r = TCL_ERROR;
setlocale(LC_ALL, "C");
interp = Tcl_CreateInterp();
if (interp != NULL) {
Tcl_FindExecutable(argv[0]);
r = Tcl_Eval(interp, "source test_namespace.tcl");
if (TCL_OK == r)
r = Tcl_Eval(interp, "puts [namespace children ::bob]");
Tcl_DeleteInterp(interp);
}
return r;
}
Running the above:
C:\opt\tcl\src>cl -nologo -W3 -O2 -MD -I\opt\tcl\include test_namesp_embed.c -link -subsystem:console -release -libpath:\opt\tcl\lib tcl85.lib
test_namesp_embed.c
C:\opt\tcl\src>test_namesp_embed.exe test_namespace.tcl
Namespace calling ::bob::proc2
::bob::joe
A better embedding scheme that uses tclAppInit to extend a stock Tcl interpreter:
#include <tcl.h>
#include <locale.h>
#define TCL_LOCAL_APPINIT Custom_AppInit
int
Custom_AppInit(Tcl_Interp *interp)
{
return Tcl_Eval(interp, "source test_namespace.tcl");
}
#include "/opt/tcl/src/tcl.git/win/tclAppInit.c"
Building and running this also produces the same output as previous versions:
C:\opt\tcl\src>cl -nologo -W3 -O2 -MD -I\opt\tcl\include test_namesp_embed.c -link -subsystem:console -release -libpath:\opt\tcl\lib tcl85.lib
C:\opt\tcl\src>test_namesp_embed.exe
Namespace calling ::bob::proc2
% namespace children ::bob
::bob::joe
% exit
As far as I'm aware, the only way your code could not produce the message you expect is if the current namespace at the point it was called was other than the global namespace. Suppose the current namespace was ::foo, the first namespace eval would work in ::foo::bob and the inner one in ::foo::bob::joe. Unqualified procedure definitions put their definitions in the current namespace, of course.
To detect if this is really the case, add the output of the namespace current command to your printed message.
If this is the problem, change the outer namespace eval to use a fully-qualified name:
namespace eval ::bob { # <<<<<<< NOTE THIS HERE!
namespace eval joe {
proc proc1 {} {}
}
proc proc2 {} {
puts "proc2"
}
proc ::proc3 {} {
puts "proc3"
}
proc joe::proc4 {} {
puts "proc4"
}
}
If you're writing a Tcl package this is very strongly recommended and it is a good idea even if you're not going all the way to that extent; you can never be quite sure what context a script is going to be sourced in. (The inner namespace eval is OK though; that's running inside a known context, the outer namespace eval.)
Sorry guys for trouble. I found the problem.
The problem is in my code. I had the following procedure which I completely forgot about:
rename proc _proc
_proc proc {name args body} {
global pass_log_trace
set g_log_trace "0"
if {[info exists pass_log_trace]} {
set g_log_trace $pass_log_trace
}
# simple check if we have double declaration of the same procedure
if {[info procs $name] != ""} {
puts "\nERROR: redeclaration of procedure: $name"
}
_proc $name $args $body
if {$g_log_trace != 0} {
trace add execution $name enter trace_report_enter
trace add execution $name leave trace_report_leave
}
}
The purpose of this procedure, mainly, is to add entry and exit point tracers to all the procedures in my code. For some reason, which I'm still investigating, it also removes the namespace scoping.
Basically the problem is to execute Tcl code defined in one namespace, using calls to functions in that namespace, in an eval inside another namespace.
The following code works fine:
namespace eval ::eggs {
namespace export e1 eeval
proc e1 {} { puts pe1 }
proc eeval body { puts "in cb enter"; eval $body; puts "in cb exit" }
}
namespace import ::eggs::*
namespace eval ::spam {
namespace export s1 scall
proc scb {} { puts pscb }
proc scall {} { puts "in call enter"; eeval {::spam::scb}; puts "in call exit" }
e1
scall
}
namespace import ::spam::*
and prints:
% % % % % % pe1
in call enter
in cb enter
pscb
in cb exit
in call exit
Now I want to replace the ::spam::scb with a plain scb. I would trade in a wrapper for eeval.
My use case:
The namespace eggs is a very basic library for regression testing. The namespace spam is a small library implementing nice to have functions. They shall be tested upon reload. For this scall is called and uses a special test function in eggs called eeval.
The eeval is the unit test. For convenience and "do-not-repeat-yourself" reasons I'd like to not have to use the fully qualified namespace name of any function defined in spam.
Ideally scall would look like this:
proc scall {} { puts "in call enter"; eeval {scb}; puts "in call exit" }
However, during execution the eval in ::eggs::eeval does not know where to find scb. Since eggs is just a test library I cannot import the spam namespace.
Is there any way one could e.g. devise a wrapper for e.g. eeval to make it run in the other namespace?
If you're going to pass the code to eval (possibly appending arguments) then the simplest method to generate the callback script is with namespace code:
eeval [namespace code {scb}]
That generates all the wrapping code to ensure that things work correctly, including handling all sorts of cases you've not dealt with:
% namespace eval boo {
puts [namespace code {foo bar}]
}
::namespace inscope ::boo {foo bar}
However, if you're doing the callback immediately then the most common approach is not the above, but rather to use uplevel 1 to do the callback:
proc eeval body {
puts "in cb enter"
uplevel 1 $body
puts "in cb exit"
}
This has the advantage that the called-back code really is in the stack context of the caller, allowing it to do useful things like access local variables. Since the code is running in the same context that you define it[*], it's fine using that for resolving command names.
[*] That's a big fat lie — Tcl's semantics are rather more complex than that — but everything works like what I've said is true.
namespace current is the DRYer you're probably looking for.
namespace eval ::spam {
proc scall {} {
puts {in call 2 enter}
eeval [namespace current]::scb
puts {in call 2 exit}
}
}
Or pass the current namespace to the eeval proc
proc ::eggs::eeval {body {namespace ""}} {
puts "in cb enter"
if {$namespace == ""} {
eval $body
} else {
namespace eval $namespace $body
}
puts "in cb exit"
}
proc ::spam::scall {} {
puts "in call 3 enter"
eeval scb [namespace current]
puts "in call 3 exit"
}
Are there any packages or any specific way to support function or procedure overloading in TCL??
This is my scenario. I need to write a generic procedure that accepts two or 3 files, wherein I may or may not have the third file (File3)
proc fun { File1 File2 File3 }
{
}
proc fun { File1 File2 }
{
}
There is no overriding in tcl. The second declaration will just replace the first one.
But you handle it with a single procedure. There are two ways at least:
1) Specify the last argument with its default value. Then it will be optional when you calls the function.
proc fun { file1 file2 {file3 ""} } {
if {$file3 != ""} {
# the fun was called with 3rd argument
}
}
2) Use the special argument args, which will contain all arguments as a list. And then analyze the number of arguments actually passed to.
proc fun { args } {
if {[llength $args] == 3} {
# the fun was called with 3rd argument
}
}
Tcl doesn't really support procedure overloading, which makes sense when you consider that it doesn't really have types, per se. Everything is a string that can, depending on value, be interpreted as other types (int, list, etc).
If you can describe what it is you're trying to accomplish (why you think you need overloading), we might be able to make a recommendation about how to accomplish it.
Given the edit to your question, there's a couple different ways to go about it. GrAnd has shown 2 of them. A third, and one I'm a fan of, is to use information specifically about how the command was called:
proc fun { File1 File2 {File3 ""}} { ;# file3 has a default
if {[llength [info level 0]] == 3} { ;# we were called with 2 arguments
;# (proc name is included in [info level 0])
# do what you need to do if called as [fun 1 2]
} else { ;# called with 3 arguments
# do what you need to do if called as [fun 1 2 3]
}
}
Here is an example to hack puts, using a namespace to hide puts and :: to access built-in:
namespace eval newNameSpace {
proc puts {arg} {
set tx "ADDED:: $arg"
::puts $tx
}
puts 102
}
Another way, you can do this:
proc example {
-file1:required
-file1:required
{-file3 ""}
} {
if {$file3 ne ""} {
#Do something ...
}
}
when you call the proc
example -fiel1 info -file2 info2