Я пишу программу, которая выполняет сценарии tcl. Когда сценарий exit
команда, программа вылетает с этой ошибкой
DeleteInterpProc called with active evals
Aborted
я звоню Tcl_EvalFile(m_interpreter, script.c_str())
где скрипт это имя файла.
Также я попробовал Tcl_Eval
с аргументами интерпретатора и «исходным именем файла». Результат тот же. Другие команды tcl (например, put) интерпретатор выполняется нормально. Как это можно исправить?
#include <tcl.h>
#include <iostream>
int main() {
Tcl_Interp *interp = Tcl_CreateInterp();
//Tcl_Preserve(interp);
Tcl_Eval (interp, "exit");
//Tcl_Release(interp);
std::cout << "11111111111" << std::endl;
return 0;
}
Это простой случай. «11111111111» не печатаются. Как я понимаю вся программа закрывается при звонке Tcl_Eval (interp, "exit");
, Результат такой же после добавления Tcl_Preserve
а также Tcl_Release
,
Проблема в том, что интерпретатор, контекст выполнения для кода Tcl, удаляет свои ноги из-под себя; это очень смущает! По крайней мере, вы получаете чистую панику / прерывание, а не отвратительный, трудно воспроизводимый сбой.
Самое простое решение, вероятно, это сделать:
Tcl_Preserve(m_interpreter);
// Your code that calls Tcl_EvalFile(m_interpreter, script.c_str())
// and deals with the results.
Tcl_Release(m_interpreter);
Помните, что после Tcl_Release
, Tcl_Interp
дескриптор может относиться к удаленной памяти.
(Да, упаковка Tcl_Preserve
/Tcl_Release
в раии добро разумно.)
Если вы хотите вместо этого позволить вашему коду запускаться после того, как скрипт выполнит exit
, вы должны предпринять дополнительные шаги. В частности, стандартный Tcl exit
Команда не предназначена для возврата к вызывающему контексту: она будут вызвать процесс, чтобы вызвать _exit(2)
системный вызов. Чтобы изменить его поведение, замените его:
// A callback function that implements the replacement
static int
MyReplacementExit(ClientData unused, Tcl_Interp *interp, int argc, const char *argv[])
{
// We ought to check the argument count... but why bother?
Tcl_DeleteInterp(interp);
return TCL_OK;
}
int main() {
Tcl_Interp *interp = Tcl_CreateInterp();
// Install that function over the standard [exit]
Tcl_CreateCommand(interp, "exit", MyReplacementExit, NULL, NULL);
// Important; need to keep the *handle* live until we're finished
Tcl_Preserve(interp);
// Or run whatever code you want here...
Tcl_Eval(interp, "exit");
// Important piece of cleanup code
if (!Tcl_InterpDeleted(interp))
Tcl_DeleteInterp(interp);
Tcl_Release(interp);
// After this point, you *MUST NOT* use interp
std::cout << "11111111111" << std::endl;
return 0;
}
Правила управления памятью в подобных сценариях изложены в страница руководства для Tcl_CreateInterp
. (Это справочная страница 8.6, но соответствующие правила были верны, по крайней мере, начиная с Tcl 7.0, что более двух десятилетий назад.) После удаления интерпретатора вы больше не можете рассчитывать на выполнение каких-либо команд или доступ к любым переменным в нем; библиотека Tcl обрабатывает состояние для вас.
Может быть лучше заменить (скрыть) exit
командовать и создать свой собственный exit
Команда, которая завершит вашу программу изящно. Я не очень хорош с C и Tcl C Api, но я надеюсь, что это поможет вам.
Eggdrop, например, использует die
Команда выйти изящно.