在 C 程序中重复使用 TCL 解释器
Using the TCL interpreter repeatedly inside a C program
我希望多次使用c程序运行 TCL解释器多次。出于复杂的原因,我需要它是一个纯 C 程序,而不是作为共享对象嵌入的程序。例如,我希望 运行 这个简单的 tcl 程序,tryMe.tcl,两次:
prtstr "Test from tryMe.tcl"
prtstr 是我编写的一个 TCL 函数,现在只写入标准输出。下面是试图解释 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] == '[=13=]')
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);
}
非常感谢!
您应该查看 the Tcler's Wiki on this,因为在您的应用程序中嵌入 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 语言中,这是 必不可少的,因为您没有异常可以为您完成繁重的错误处理工作。
感谢您指向 TCLer 的 Wiki。那有帮助。没看懂 Tcl_Eval(interp,script)
里面的 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] == '[=10=]')
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);
}
我希望多次使用c程序运行 TCL解释器多次。出于复杂的原因,我需要它是一个纯 C 程序,而不是作为共享对象嵌入的程序。例如,我希望 运行 这个简单的 tcl 程序,tryMe.tcl,两次:
prtstr "Test from tryMe.tcl"
prtstr 是我编写的一个 TCL 函数,现在只写入标准输出。下面是试图解释 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] == '[=13=]')
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);
}
非常感谢!
您应该查看 the Tcler's Wiki on this,因为在您的应用程序中嵌入 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 语言中,这是 必不可少的,因为您没有异常可以为您完成繁重的错误处理工作。
感谢您指向 TCLer 的 Wiki。那有帮助。没看懂 Tcl_Eval(interp,script)
里面的 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] == '[=10=]')
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);
}