I'm trying to replace an old Tcl interface to C++ using SWIG. Here is an example class:
class test {
std::string str;
public:
test(const char * s):str(s) {}
void print() const {std::cout << str << std::endl;}
};
and here is the standard way to use it:
load ./example.so example
test s "this is a test string"
s print
But I want to preserve the simplicity of the old interface which does not use the "". I've found that I can do something like:
load ./example.so example
proc TEST {args} { test [lindex $args 0] [lrange $args 1 end] }
TEST s2 this is another test string
s2 print
Which looks simple and works flawlessly, however, of course, I cannot have the proc definition in the user script. I'm not sure where else I could place it. Is there a way to put it in the .i file?
I think you can put this in your .i file:
%init %{
Tcl_Eval(interp, "proc TEST {args} { ... }");
%}
That will insert a call to Tcl_Eval (which will make the procedure) into your module's initialization function, and as long as the interp context is available there (WARNING! Check this!) then you'll get everything nicely packaged.
If you've got lots of code, consider calling Tcl_EvalFile instead, or calling Tcl code that will source the script code (necessary if you've got a need for complex scripting to locate the file to read).
Related
In the tk code base I found the construct:
proc ::tk::dialog::file::chooseDir:: {args} {
Normally I would expect the procedure name after the last set of :: but here it is empty. Is this some sort of constructor in a namespace?
(Might look like a trivial question but I'm not a tcl programmer and need to know it to, automatically, generate some documentation.
Some more of the code (maybe gives some background, it is the beginning of the file)
namespace eval ::tk::dialog {}
namespace eval ::tk::dialog::file {}
namespace eval ::tk::dialog::file::chooseDir {
namespace import -force ::tk::msgcat::*
}
proc ::tk::dialog::file::chooseDir:: {args} {
variable ::tk::Priv
set dataName __tk_choosedir
upvar ::tk::dialog::file::$dataName data
Config $dataName $args
...
Normally I would expect the procedure name after the last set of ::
but here it is empty
The empty string is a valid name for a procedure in Tcl (as it for variables).
% namespace eval ::tk::dialog::file::chooseDir {}
% proc ::tk::dialog::file::chooseDir:: {args} { return "called!" }
% ::tk::dialog::file::chooseDir::
called!
% namespace eval ::tk::dialog::file::chooseDir { "" }
called!
% info procs ::tk::dialog::file::chooseDir::*
::tk::dialog::file::chooseDir::
I don't know the history behind these Tk internals, but a procedure named using the empty string might be the main procedure for the same-named namespace chooseDir (as a kind of naming convention), rather than just duplicating the name: proc ::tk::dialog::file::chooseDir::chooseDir {args} {;}. Or, it is because the entire directory-picking functionality is auto_loaded, which requires a proc (command) name rather than a namespace name?
automatically, generate some documentation.
Maybe, when harvesting a Tcl interpreter for pieces to document, take the containing namespace name chooseDir as the documented name of such a procedure?
In Python, Ruby 2.0, Perl 6, and some hardware description languages, one can use named arguments. See this example. This makes the code more readable, easy to maintain, etc. Is there a way of getting it done/extension, in TCL 8.6, other than using a dictionary as a workaround?
In 8.6, use a dictionary parsed from args. The dict merge command can help:
proc example args {
if {[llength $args] % 2} {
return -code error "wrong # args: should be \"example ?-abc abc? ?-def def?\""
}
set defaults {
-abc 123
-def 456
}
set args [dict merge $defaults $args]
set abc [dict get $args -abc]
set def [dict get $args -def]
puts "abc=$abc, def=$def"
}
example; # abc=123, def=456
example -abc 789; # abc=789, def=456
example -def 789; # abc=123, def=789
example -def 246 -abc 135; # abc=135, def=246
You can go further than that with verifying (the tcl::prefix command can help) but it's a lot more work and doesn't buy you a lot more in production code. Not that that has stopped people from trying.
There are two proposals to add full named argument handling
(TIP #457, TIP #479) to 8.7 at the moment, but I'm not sure that either have really gained traction. (The problem from my perspective is the extra runtime cost that has to be borne by code that doesn't volunteer to support named arguments. There might be other issues too, such as disagreement over preferred syntax; I've not paid so much attention to that as I'm still fretting over the performance implications in a pretty hot piece of code.)
There is an entire page on the tcler's wiki that discusses named arguments: http://wiki.tcl.tk/10702
You can do it yourself with a little creativity. There are several mechanisms that allow you to do this:
procs can define other procs
proc behave just like a proc (the function definition system is not a syntax, it is a function call)
procs can use the args argument instead of positional parameter and manually process the list of arguments
you can execute code in any parent stack frame using uplevel
you can pull variables from any parent stack frame using upvar
everything is a string!
etc.
I'm not sure I've listed all the possible mechanisms.
My personal implementation of this idea is optproc: http://wiki.tcl.tk/20066
The implementation itself is quite simple:
proc optproc {name args script} {
proc $name args [
string map [list ARGS $args SCRIPT $script] {
foreach var {ARGS} {
set [lindex $var 0] [lindex $var 1]
}
foreach {var val} $args {
set [string trim $var -] $val
}
SCRIPT
}
]
}
I basically used string manipulation (string map) to directly insert the function body ($script) into the defined function without any substitutions etc. This is to avoid any $ or [] from being processed. There are many ways to do this but my go-to tool is string map.
This is similar to Donald's answer except for two things:
I don't transform args into a dict instead I manually process args and declare each local variable in a loop.
This is a meta solution. Instead of processing args I created another function (syntax) to create a function that processes args.
Usage (stolen from Donald's answer) would be:
optproc example {abc def} {
puts "abc=$abc, def=$def"
}
But note that my solution is not the only solution. There are many ways to do this limited only by creativity.
Can any one help me in TCL programming, I am new to TCL
I would like to create functions like
employee_data Name() Dept() Tasks() ...
suppose i need execute from above function like employee_data Name() Tasks() ...
here i want skip Dept() arguments,
I tried to create function like but it does not work out..
proc employee_data {
Name (A B C....)
Dept (a b c....)
Tasks (s d f...)
} employee_data;
proc employee_data { Name($A $B $C) Dept($a $b $b) Tasks ($s $d $f) } {
Body...
}
Thank you very much.
I believe the basic misunderstanding is how you call/invoke Tcl functions/commands.
Unlike many other languages, where you invoke a function with func(arg1,arg2,"arg3",arg4) Tcl uses func $arg1 $arg2 "arg3" $arg4, where arg1, arg2 and arg4 are variables.
To define such a function, use proc. The syntax is
proc sum {a b} {
return [expr {$a + $b}]
}
a b is the arguments list. Note that you don't need to declare the function.
I don't exactly understand what you are trying to do here, but it looks more or less like a struct/class for me.
Just so as you know, Tcl and C++ have very different approaches to values and types. In particular, Tcl works almost entirely by logically-immutable references to values that are nevertheless type-mutable, which is very different to C++ (and many other languages, to be fair). True mutator operations (such as increments) actually create copies.
Another key difference is that in Tcl, everything is done by executing commands. That includes creating other commands. That proc? It's a call to a command (called proc) which immediately creates a procedure with the given name, arguments and body. There's nothing like declaration; stuff happens when you tell the code to make it happen. This sounds rather more complicated than it is.
How to pass list values into procedures
Suppose your Name was a list, you'd then pass the whole list value in (by immutable reference, so fast and safe) and the code inside could do whatever it wants without affecting the outside world. You'd then write that like this:
# Define
proc employee_data {Name ...} {
lassign $Name A B C
# ... do other processing ...
}
# Call
set Name {Aitem Bitem Citem}
employee_data $Name ...
You could also call with immediately-defined data; Tcl's entirely happy with this:
employee_data {Aitem Bitem Citem} ...
Passing in dictionary values
Dictionaries are very much like lists, except they map keys to values instead of being sequences.
proc employee_data {Name ...} {
dict with Name {}
# ...
}
set Name {A "foo" B "bar" C "grill"}
employee_data $Name ...
You can also pass a copy of the contents of an (associative) array as a dictionary value, like this:
employee_data [dict get Name] ...
Passing references
But suppose you wanted to mutate the outside world! To do that, you have to pass the name of the variable to change. You then use upvar to bind a local variable to the caller's variable so that you can do the modifications and have them stick. The upvar command lets you do in effect call-by-name, and it's highly magical.
proc employee_data {NameVar ...} {
upvar $NameVar name
# ... accessing $name as if it was whatever variable was passed ...
}
set Name {Aitem Bitem Citem}
# Note: *not* $Name! We want the name, not the contents!
employee_data Name ...
You can think of built-in commands like set and incr as working like this. And this works for associative arrays too.
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"
}