在C程序中反复使用TCL解释器

时间:2019-03-18 22:07:25

标签: c tcl

我希望多次使用c程序反复运行TCL解释器。由于复杂的原因,我需要使它成为纯C程序,而不是作为共享对象嵌入的东西。例如,我希望两次运行这个简单的tcl程序tryMe.tcl:

prtstr "Test from tryMe.tcl"

prtstr是我已经编写的TCL函数,目前仅写入stdout。下面是试图两次解释tryMe.tcl程序的C代码。

我在linux下像下面这样编译程序:

$ gcc -c try.c; gcc -o try try.o -ltcl; 

并像这样运行它:

$ ./try tryMe.tcl

我得到零输出。我究竟做错了什么?另外,还需要执行一些步骤来重置tcl解释器,以便每次都可以刷新。

#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);
}

非常感谢!

2 个答案:

答案 0 :(得分:0)

您应该签出documentation,因为在您的应用程序中嵌入Tcl解释器的模式是一种已知且受支持的模式。它包含一个可以适应的工作示例(不,我没有写;我更喜欢扩展标准的Tcl解释器)。

您遇到的主要问题是您没有打电话给Tcl_FindExecutable()。在现代Tcl中,这会初始化库中的许多关键子系统(包括其高性能内存分配器!),因此它至关重要。在您的情况下,您可以使用真实的argv,因此可以将argv[0]与之配合使用:

Tcl_FindExecutable(argv[0]);
// NULL would also work as an argument, in a pinch at least

完成此操作后,就可以调用其他Tcl API函数,特别是Tcl_CreateInterp()

您有一个小问题,因为您没有测试失败的呼叫结果。在C语言中,这是必不可少的,因为您没有例外来为您进行繁重的错误处理。

答案 1 :(得分:0)

感谢指向TCLer Wiki的指针。有帮助。我不了解script中的Tcl_Eval(interp,script)不是文件名,而是包含tcl程序的字符串。因此,该程序使用TCL_Evalfile(),我也希望能够将命令行参数传递给tcl程序。我发现了如何深入Tcl_MainEx()的TCL源。以下是执行我想要的程序。我还发现,多次调用Tcl_EvalFile确实可以保留状态,因此,如果我想获得新的解释器值,则必须删除旧的解释器,并每次都创建一个新的解释器。

#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);
}