DeleteInterpProc调用活动的评估

DeleteInterpProc called with active evals

本文关键字:评估 活动 调用 DeleteInterpProc      更新时间:2023-10-16

我正在编写一个执行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_PreserveTcl_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包裹在RAII中是合理的。)


如果您希望允许您的代码在脚本执行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的手册页,但至少从20多年前的Tcl 7.0开始,相关规则就已经成立了。)一旦解释器被删除,你就不能再指望执行任何命令或访问其中的任何变量;

最好替换(隐藏)exit命令并创建自己的exit命令,以便优雅地退出程序。我不太擅长C和Tcl C Api,但我希望这能帮助你。

以Eggdrop为例,使用die命令优雅退出。