Using the TCL interpreter repeatedly inside a C program

对着背影说爱祢 提交于 2019-12-25 03:02:05

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!