#c #tcl
#c #tcl
Вопрос:
Я хочу использовать программу на c, которая многократно запускает интерпретатор TCL много раз. По сложным причинам мне нужно, чтобы это была чистая программа на C, а не что-то, что встроено в виде общего объекта. В качестве примера я хочу дважды запустить эту простую программу tcl, tryMe.tcl:
prtstr "Test from tryMe.tcl"
Prtstr — это функция TCL, для которой я написал прямо сейчас, просто записывающая в стандартный вывод. Ниже приведен код на c, который пытается дважды интерпретировать программу tryMe.tcl.
Я компилирую приведенную ниже программу следующим образом под 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, amp;len);
if (str[0] == '')
return TCL_ERROR;
printf("len: %d, str: %sn", 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(amp;cmd, "%s -x -y -z", argv[1]);
Tcl_Eval(interp, cmd);
free(cmd);
asprintf(amp;cmd, "%s -q -r -s 2", argv[1]);
Tcl_Eval(interp, cmd);
exit(0);
}
Большое спасибо!
Комментарии:
1. Помимо Tcl, пользователи могут захотеть изучить язык сценариев Pawn, который также является небольшим и легко встраиваемым. Я искал в Tcl что-то вроде, но чувствовал, что Pawn подходит лучше. Lua — это другой язык, который также используется аналогичным образом.
Ответ №1:
Вам следует ознакомиться с вики-сайтом Tcler по этому вопросу, поскольку шаблон встраивания интерпретатора 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 это важно, поскольку у вас нет исключений, которые выполняли бы тяжелую работу по обработке ошибок за вас.
Ответ №2:
Спасибо за указатель на Wiki TCLer. Это помогло. Я не понял, что script
in Tcl_Eval(interp,script)
— это не имя файла, а символьная строка, содержащая программу tcl. Итак, эта программа использует TCL_Evalfile()
Я также хотел иметь возможность передавать аргументы командной строки программе tcl. Я узнал, как, но погрузился в исходный код TCL для Tcl_MainEx(). Ниже приведена программа, которая делает то, что я хочу. Также я обнаружил, что при вызове 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, amp;len);
if (str[0] == '')
return TCL_ERROR;
printf("len: %d, str: %sn", 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 interpretern");
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);
}
Комментарии:
1. «итак, если я хочу новое значение интерпретатора, мне придется удалять старое и каждый раз создавать новое». Да и нет, вам не обязательно получать новый основной интерпретатор из C для каждого шага; вы можете управлять жизненным циклом из своего скрипта (
tryMe.tcl
) с помощью дочернего или ведомого интерпретатора:set slave [interp create]; $slave eval {prtstr "Test from tryMe.tcl"}; interp delete $slave
2. Верно, что вы можете создать дочерний интерпретатор, чтобы избежать необходимости создавать / удалять основной интерпретатор в коде C, но с моими измерениями это происходит медленнее. Сохранение интерпретатора C / создание удаления в дочернем интерфейсе TCL = 1,7 секунды. Новое прерывание C на 1,1 секунды.