问题
I wish to use a c program repeatedly run the TCL interpreter many times. For reasons that are complicated, I need this to be a pure C program and not something that is embedded as a shared object. As an example, I wish to run this simple tcl program, tryMe.tcl, twice:
prtstr "Test from tryMe.tcl"
The prtstr is a TCL function that I have written that for right now just writes to stdout. Below is the c code that is attempting to interpret the tryMe.tcl program twice.
I compile the program below like this under linux:
$ gcc -c try.c; gcc -o try try.o -ltcl;
and run it like this:
$ ./try tryMe.tcl
And I get zero output. What am I doing wrong? Also are there steps required to reset the tcl interpreter so that it will be fresh each time.
#define _GNU_SOURCE
#include <tcl/tcl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int PrintStrObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *str;
int len;
Tcl_Obj *objPtr;
int i;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "value");
return TCL_ERROR;
}
objPtr = objv[1];
str = Tcl_GetStringFromObj(objPtr, &len);
if (str[0] == '\0')
return TCL_ERROR;
printf("len: %d, str: %s\n", len, str);
return TCL_OK;
}
int Tcl_AppInit(Tcl_Interp* interp)
{
if (Tcl_Init(interp) == TCL_ERROR)
return TCL_ERROR;
Tcl_CreateObjCommand(interp,"prtstr", PrintStrObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
int main(int argc, char *argv[])
{
char *cmd = NULL;
Tcl_Interp * interp = Tcl_CreateInterp();
Tcl_AppInit(interp);
asprintf(&cmd, "%s -x -y -z", argv[1]);
Tcl_Eval(interp, cmd);
free(cmd);
asprintf(&cmd, "%s -q -r -s 2", argv[1]);
Tcl_Eval(interp, cmd);
exit(0);
}
Thanks very much!
回答1:
You should check out the Tcler's Wiki on this, as the pattern of embedding a Tcl interpreter in your application is a known and supported one. It includes a worked example that you can adapt (and no, I didn't write it; I prefer to extend standard Tcl interpreters).
The main problem you've got is that you're not calling Tcl_FindExecutable(). In modern Tcl, that initialises a number of key subsystems in the library (including its high-performance memory allocator!) so it's slightly vital. In your case, you've got a real argv available so you can use argv[0] with it:
Tcl_FindExecutable(argv[0]);
// NULL would also work as an argument, in a pinch at least
Once you've done that, you can call other Tcl API functions, specifically Tcl_CreateInterp().
You have a minor problem in that you are not testing the results of calls for failure. In C, this is essential as you don't have exceptions to do the heavy lifting of error handling for you.
回答2:
Thanks for the pointer to TCLer's Wiki. That helped. I didn't understand that the script in Tcl_Eval(interp,script) was not a file name but a character string containing a tcl program. So this program uses TCL_Evalfile() I also wanted to be able to pass command line arguments to the tcl program. I found out how but diving into the TCL source for Tcl_MainEx(). Below is a program that does what I want. Also I discovered that calling Tcl_EvalFile more than one does retains the state so if I want a fresh value of the interpreter, I'll have to delete the old one and create a new one everytime.
#include <tcl/tcl.h>
#include <stdio.h>
#include <stdlib.h>
int PrintStrObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *str;
int len;
Tcl_Obj *objPtr;
int i;
if (objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "value");
return TCL_ERROR;
}
objPtr = objv[1];
str = Tcl_GetStringFromObj(objPtr, &len);
if (str[0] == '\0')
return TCL_ERROR;
printf("len: %d, str: %s\n", len, str);
return TCL_OK;
}
int Tcl_AppInit(Tcl_Interp* interp)
{
if (Tcl_Init(interp) == TCL_ERROR)
return TCL_ERROR;
Tcl_CreateObjCommand(interp,"prtstr", PrintStrObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
int main(int argc, char **argv)
{
char *script = argv[1];
Tcl_Obj *argvPtr;
Tcl_FindExecutable(script);
Tcl_Interp *interp = Tcl_CreateInterp();
if (interp == NULL) {
fprintf(stderr,"Cannot create TCL interpreter\n");
exit(-1);
}
if (Tcl_AppInit(interp) != TCL_OK)
return TCL_ERROR;
Tcl_SetVar2Ex(interp, "argv0", NULL, Tcl_NewStringObj(script,-1), TCL_GLOBAL_ONLY);
argc -= 2;
argv += 2;
Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewIntObj(argc), TCL_GLOBAL_ONLY);
argvPtr = Tcl_NewListObj(0, NULL);
while (argc--)
Tcl_ListObjAppendElement(NULL, argvPtr, Tcl_NewStringObj(*argv++, -1));
Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY);
if (Tcl_EvalFile(interp, script) != TCL_OK)
return TCL_ERROR;
exit(0);
}
来源:https://stackoverflow.com/questions/55230757/using-the-tcl-interpreter-repeatedly-inside-a-c-program