I'm working on a project, for which i have to call a tcl-procedure
(out of mytclscript.tcl) from a C programm. It's the very first time
I'm working with tcl, and I'm getting some kind of desperated, since I
can't find the right way to do it. The tcl scripts "mytclscript.tcl"
shoul be as follows:
mytclscript.tcl:
proc A { result_var_name argument1 argument2 } {
set result_var [ compute_result argument1 argument2 ]
}
The C-Programm has to call the tcl-procedure "A" with the arguments
needed ("result_var_name", "argument1", "argument2") and
will further work with the result string in "result_var". I could
manage it to create and initialize the tcl-interpreter and call the
tcl-script with Tcl_EvalFile, but I have no idea on how to pass the
arguments from the C-Program to the Tcl-Procedure, and on how to get
the result value from the Tcl-Procedure back to the C-Function.
Does anybody know where I can find a chunk of C and Tcl-Code wich
handles a similar problem, or where I can find some hint on how to do
it? I have been looking at the Welch Tcl and Tk book, but I can only
find hints on how to call C from Tcl, and not on how to call Tcl from
C... Any kind of help would be hugely appreciated... I'm relly on the
brink of a nervous breakdown... :}
Thankfully,
Julia
--
++++++++++++++++++++++++++++++++++++++
Julia Fischer
Hochstrasse 33
D-91093 Hessdorf
E-Mail: jul.f...@gmx.de
++++++++++++++++++++++++++++++++++++++
The easiest way to use various flavours of Tcl_Eval, such as
if (Tcl_VarEval(interp,"A {",result_var_name,"} {",argument1,"} {",
argument2,"}; set {",result_var_name,"}",0)!=TCL_OK
) {
error processing
}
string_result = Tcl_GetStringResult(interp);
You'll usually get better performance with Tcl_Obj and related call like
Tcl_EvalEx, but the string oriented interface will also work. If you don't
want to plunge into Tcl's objects yet.
--
Derk Gwen http://derkgwen.250free.com/html/index.html
I'm not even supposed to be here today.
Here is some code of mine:
sprintf( string_command, "%s %s", string, "specify" ) ;
DM_EvalScriptCommand( string_command ) ;
string holds the name of the proc (which can be read from a
user-interface window) and "specify" is the only argument.
My routine DM_EvalScriptCommand() isolates the rest of the
code from the Tcl interpreter routines. It is like this:
int
DM_EvalScriptCommand(
char * command )
{
if ( Tcl_Eval( tcl_interp, command ) == TCL_OK )
{
return DM_Ok ;
}
else
{
return DM_Error ;
}
}
Of course, tcl_interp is a static variable that has
been initialised before and I have sourced a small
script library with Tcl_EvalFile() as part of the
initialisation but not much more.
Regards,
Arjen
and then calling
Tcl_Eval(interp,"$command $sr_lid $sr_aid $sr_vid")
as you proposed. It's working!!! :) Nevertheless, I don't really like
the dependencies which arise when working with global variables...
particularly because the tcl-script and the C-Programm will be
developed on a longer term
by different groups...
Thanks a lot, Arjen!! :)
Arjen Markus <arjen....@wldelft.nl> wrote in message news:<3EC8A783...@wldelft.nl>...
thank's a lot for the hint (and sorry that I'm responding with a
little delay :} ). Well, actually, now that my string oriented
approach is working quite well, I'm seriously thinking about using a
Tcl_Obj interface (cf. also answer to Arjen). In fact I got startet
with a string-interface only as a first approach for tcl-dummies+, but
now, that I'm almost an expert... ;) Well, let's say I'm a dummy- :).
Ok. Seriously. Could you give me a hint on how to create Tcl_Objs for
the commando and arg values, and call the tcl-script from the
C-function with these Objects? One of the objects will be used in the
tcl-procedure to store a result-string value, which will be used in
the C-function afterwards. Is it possible to get and use the string
value of this result-object in the C-function? (I bet it is, and I bet
you know how... :)
Thanks a lot for your help!!!!
Regards,
Julia
Derk Gwen <derk...@HotPOP.com> wrote in message news:<vch8m4t...@corp.supernews.com>...
If you want to construct a single command, I would suggest constructing it as a
list, since that will do all the argument quoting for you. There are a couple of
ways to create list. One method would be
int rc; char *stringValue;
Tcl_Obj *command = Tcl_NewObj();
Tcl_IncrRefCount(command);
Now add the command name
Tcl_ListObjAddElement(0,command,Tcl_NewStringObj("command-name",-1));
and then the various arguments
Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("argument-1",-1));
Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("argument-2",-1));
...
(If you already have the command name or argument values as objects, it's just
Tcl_ListObjAppendElement(0,command,objectPointer);'
instead of creating a new string object.)
And then evaluate the command with perhaps
int rc = Tcl_EvalObjEx(interp,command,TCL_EVAL_DIRECT);
Tcl_DecrRefCount(command);
if (rc==TCL_OK) {
Tcl_Obj *objValue = Tcl_ObjGetVar2(interp,varNameObj,0,TCL_LEAVE_ERR_MSG);
if (objValue) stringValue = Tcl_GetString(objValue);
else rc = TCL_ERROR;
}
if (rc==TCL_OK) {
stringValue has the string value of the variable
}else {
interp has the error message
}
If you wanted to implement a command like "last List" that does "lindex List end",
you can do something like
static int lastElementCommand(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
) {
int rc = TCL_ERROR;
if (objc<2) {
Tcl_WrongNumArgs(interp,objc,objv,"List");
}else if (objc>2) {
Tcl_WrongNumArgs(interp,2,objv,"");
}else {
Tcl_Obj *command = Tcl_NewObj(); Tcl_IncrRefCount(command);
Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("lindex",-1));
Tcl_ListObjAppendElement(0,command,objv[1]);
Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("end",-1));
rc = Tcl_EvalObjEx(interp,command,TCL_EVAL_DIRECT);
Tcl_DecrRefCount(command);
}
return rc;
}
--
Derk Gwen http://derkgwen.250free.com/html/index.html
If you plan to shoplift, let us know.
Thanks
Hello Julia,
I know there were already solutions, and one "90%" satisfying
solution, but I want to spend mine, too.
I wrote a library of functions supporting tcl C-API usage and to mask
the usage of tcl commands in C. So I wrote some functions only about
executing or evaluating tcl commands, procedures, scripts from C:
int tcl_evalf ( Tcl_Interp *ip, const char *cmd, ... );
int tcl_evalObjf ( Tcl_Interp *ip, Tcl_Obj *cmd, ... (char *)
0 );
int tcl_globalEvalf ( Tcl_Interp *ip, const char *cmd, ... );
int tcl_globalEvalObjf( Tcl_Interp *ip, Tcl_Obj *cmd, ... (char *)
0 );
Usage example:
1. usage á la printf:
double dValue, dExprResult;
char *pocMode;
dValue = 90.0;
dExprResult = 0.0;
pocMode = "sin";
if ( ( tcl_evalf(
ip,
"expr {%s(%lf)}", pocMode, dValue
) == TCL_ERROR ) ||
( Tcl_GetDoubleFromObj(
ip,
Tcl_GetObjResult( ip ),
&dExprResult
) == TCL_ERROR ) )
{
/* error handling */
}
printf( "expr {%s(%lf)} = %lf", pocMode, dValue, dExprResult );
2. usage with tcl objects:
int varExists( ip, char *pocVarName, int *bExists )
{
if ( !bExists )
return TCL_ERROR;
static Tcl_Obj *poInfoCmd = 0;
static Tcl_Obj *poInfoOption = 0;
if ( !poInfoCmd )
{
poInfoCmd = Tcl_NewStringObj( "info", -1 );
poInfoOption = Tcl_NewStringObj( "exists", -1 );
Tcl_IncrRefCount( poInfoCmd );
Tcl_IncrRefCount( poInfoOption );
}
int iResult;
Tcl_Obj *poVarName;
iResult = TCL_ERROR;
poVarName = Tcl_NewStringObj( varName, -1 );
Tcl_IncrRefCount( poVarName );
if ( tcl_evalObjf(
ip,
poInfoCmd, poInfoOption, poVarName,
NULL
) == TCL_OK )
{
if ( Tcl_GetBooleanFromObj(
ip,
Tcl_GetObjResult( ip ),
bExists
) == TCL_OK )
iResult = TCL_OK;
else
*bExists = -1;
}
Tcl_DecrRefCount( poVarName );
return iResult;
}
The following functions are developed to execute not a tcl command or
procedure via the Tcl_(Global)EvalObj API, but to get the function
pointer behind the tcl command or procedure to execute this function
like any other:
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd );
int tcl_execCmd( Tcl_Interp *ip, Tcl_Obj *poCmd );
int tcl_execCmdf( Tcl_Interp *ip, const char *pocCmd, ... );
int tcl_execCmdf( Tcl_Interp *ip, Tcl_Obj *poCmd , ... (char *) 0
);
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd, const char *arg
);
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd, Tcl_Obj *arg
);
int tcl_execCmd( Tcl_Interp *ip, Tcl_Obj *poCmd , Tcl_Obj *arg
);
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd,
int argc, const char *argv[] );
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd,
int argc, Tcl_Obj *argv[] );
int tcl_execCmd( Tcl_Interp *ip, Tcl_Obj *poCmd ,
int argc, Tcl_Obj *argv[] );
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd,
const char *pocOpt, const char *arg );
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd,
const char *pocOpt, Tcl_Obj *arg );
int tcl_execCmd( Tcl_Interp *ip, Tcl_Obj *poCmd ,
Tcl_Obj *poOpt, Tcl_Obj *arg );
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd,
const char *pocOpt, int argc, const char *argv[] );
int tcl_execCmd( Tcl_Interp *ip, const char *pocCmd,
const char *pocOpt, int argc, Tcl_Obj *argv[] );
int tcl_execCmd( Tcl_Interp *ip, Tcl_Obj *poCmd ,
Tcl_Obj *poOpt, int argc, Tcl_Obj *argv[] );
If you or somebody else want to have the sources, than don't hesitate
to ask!
They are not so big, but to big to publish with this message!
Best regards,
Martin Lemburg
Thanks a lot, Derk! I will try it this way!!!
Thanks a lot, Martin, this seems to be very useful to what I'm trying
to do! I will check with my project partners if we can use your
library, and if so, I will ask you to send me the code, if possible.
Thank you! :)
Best regards,
Julia
I think I'm quite close to succeeding (following your great code
example), but I do need your help again... :} (I swear I did try it by
myself for a long time now, but I have really reached a "dead end"...
:|
The problem is that I don't really know how to access the value of the
varNameObj:
> Tcl_Obj *objValue = Tcl_ObjGetVar2(interp,varNameObj,0,TCL_LEAVE_ERR_MSG);
**********
When I build the tcl commando list by
> and then the various arguments
>
> Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("argument-1",-1));
I add the name of the varNameObj as first argument to the argument
list:
Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("varName",-1));
In the tcl-procedure itself, then, I set a reference to the varName by
using
upvar:
upvar 0 $args(1) result_var
It's working, at the end of the tcl-procedure, $result_var holds the
result (a string).
The question now is, how can I access the value of 'result_var' from
the c-function??? I did try it by doing the following:
Tcl_Obj *arg1 = Tcl_NewStringObj("varName",-1);
:
Tcl_ListObjAppendElement(0,command,arg1);
:
rc = Tcl_EvalObjEx(interp,command,TCL_EVAL_DIRECT);
:
if (rc == TCL_OK) {
Tcl_Obj *objValue =
Tcl_ObjGetVar2(interp,arg1,0,TCL_LEAVE_ERR_MSG);
---> at this point I get a memory access error message, and I have not
enough brain mass to find out WHY... :}
Please, Derk, help me!!!!
Thanks a lot, I kiss your feet!!! :)
Julia
>Hi Derk!!! :)
>
>I think I'm quite close to succeeding (following your great code
>example), but I do need your help again... :} (I swear I did try it by
>myself for a long time now, but I have really reached a "dead end"...
>:|
>
>The problem is that I don't really know how to access the value of the
>varNameObj:
>
>> Tcl_Obj *objValue = Tcl_ObjGetVar2(interp,varNameObj,0,TCL_LEAVE_ERR_MSG);
> **********
>
>When I build the tcl commando list by
>
>> and then the various arguments
>>
>> Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("argument-1",-1));
>
>I add the name of the varNameObj as first argument to the argument
>list:
>
> Tcl_ListObjAppendElement(0,command,Tcl_NewStringObj("varName",-1));
>
>In the tcl-procedure itself, then, I set a reference to the varName by
>using
>upvar:
>
> upvar 0 $args(1) result_var
>
>It's working, at the end of the tcl-procedure, $result_var holds the
>result (a string).
Good!
>The question now is, how can I access the value of 'result_var' from
>the c-function??? I did try it by doing the following:
Just end your proc with
return $result_var
and in your C code add
Tcl_GetStringResult( interp)
This function will return the result as a string - there are other
functions to handle other return types.
> Tcl_Obj *arg1 = Tcl_NewStringObj("varName",-1);
> :
> Tcl_ListObjAppendElement(0,command,arg1);
> :
> rc = Tcl_EvalObjEx(interp,command,TCL_EVAL_DIRECT);
> :
> if (rc == TCL_OK) {
> Tcl_Obj *objValue =
>Tcl_ObjGetVar2(interp,arg1,0,TCL_LEAVE_ERR_MSG);
>
>---> at this point I get a memory access error message, and I have not
>enough brain mass to find out WHY... :}
'result_var' is probably a local variable in your proc which you
cannot access from C
>
>Please, Derk, help me!!!!
>
>Thanks a lot, I kiss your feet!!! :)
You exagerate :)
HTH
Helmut Giese
If you have the variable name in an object, you can use Tcl_ObjGetVar2 to get
its value. But if you only have it in a string, rather than creating an object
just for this call, you can use Tcl_GetVar2Ex which takes strings as the
variable name and return an object pointer.
Tcl_Obj *objValue = Tcl_GetVar2Ex(interp,"varName",0,TCL_LEAVE_ERR_MSG);
# The question now is, how can I access the value of 'result_var' from
# the c-function??? I did try it by doing the following:
#
# Tcl_Obj *arg1 = Tcl_NewStringObj("varName",-1);
# :
# Tcl_ListObjAppendElement(0,command,arg1);
# :
# rc = Tcl_EvalObjEx(interp,command,TCL_EVAL_DIRECT);
You have to be careful about reference counts. If when you create a string
object you immediately append it to a list, the list takes cares of that.
When the list object reference count is freed, the string object is also freed.
If you want to use the string object as both a list element and as string
independently, you have to increment and decrement its reference count
independently.
The man page for Tcl_NewObj talks about this.
In that case you would need something like
Tcl_Obj *command = Tcl_NewObj();
Tcl_Obj *arg1 = Tcl_NewStringObj("varName",-1);
Tcl_IncrRefCount(command);
Tcl_IncrRefCount(arg1);
...
Tcl_ListObjAppendElement(0,command,arg1);
...
rc = Tcl_EvalObjEx(interp,command,TCL_EVAL_DIRECT);
Tcl_DecrRefCount(command);
if (rc == TCL_OK) {
Tcl_Obj *objValue = Tcl_ObjGetVar2(interp,arg1,0,TCL_LEAVE_ERR_MSG);
...
} else {
...
}
Tcl_DecrRefCount(arg1);
Reference counting is tricky, I was trying to use the objects in a way to minimise
worrying them. In my way of thinking about it, the Tcl_EvalObjEx segment needs to
own a pointer to the command object, and it asserts its ownership with bracketting
Tcl_IncrRefCount/Tcl_DecrRefCount near where the pointer is first acquired until
after it is no longer needed. The Tcl_ObjGetVar2 segment needs to own a pointer
to the arg1 object, and answer ownership with its own Tcl_IncrRefCount/Tcl_DecrRefCount
bracket.
A list object asserts its ownership of an element object by doing an internal
Tcl_IncrRefCount in Tcl_ListObjAppendElement, and an internal Tcl_DecrRefCount
when the element is disassociated from the list.
--
Derk Gwen http://derkgwen.250free.com/html/index.html
No pleasure, no rapture, no exquiste sin greater than central air.
thanks a lot!!! I incremented and decrement the reference count of
'arg1', as you told me, and, additionally, in the tcl-procedure I made
the variable by that name accessible at a global level, adding '#' to
the upvar call:
upvar #0 $args(1) result_var
It's working now!!! You are great!!! :) :)
thank's for your tips! :) Indeed, $result_var was only locally
accessible. I made it global by adding '#' to the upvar call:
upvar #0 $args(1) result_var
> Just end your proc with
> return $result_var
> and in your C code add
> Tcl_GetStringResult( interp)
> This function will return the result as a string - there are other
> functions to handle other return types.
The point is that I want the tcl-procedure to return an int
ERROR_CODE, that's why I do a 'call by reference' so to speak :) It's
working now, cf. my answer to Derk's last contribution.
> >Thanks a lot, I kiss your feet!!! :)
> You exagerate :)
I do not!!! It's serious! ;)
br J.