Повторное использование интерпретатора TCL внутри программы на Си - PullRequest
0 голосов
/ 19 марта 2019

Я хочу использовать программу c, многократно запускать интерпретатор TCL. По сложным причинам мне нужно, чтобы это была чистая программа на C, а не то, что встроено в общий объект. В качестве примера я хочу запустить эту простую tcl-программу, tryMe.tcl, дважды:

prtstr "Test from tryMe.tcl"

prtstr - это функция TCL, которую я написал для сейчас, просто пишет в stdout. Ниже приведен код 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, &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 голосов
/ 19 марта 2019

Спасибо за указатель на вики TCLer's. Это помогло. Я не понял, что script в 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, &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);
}


0 голосов
/ 19 марта 2019

Вы должны проверить вики 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().

У вас есть небольшая проблема в том, что вы не проверяете результаты вызовов на наличие сбоев.В Си это существенный , поскольку у вас нет исключений, чтобы выполнить тяжелую работу по обработке ошибок за вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...