问题在于解释器,即 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
,您必须采取额外的步骤。特别是,标准的 Tclexit
命令并非旨在导致返回到调用上下文:它将导致进程调用_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 开始,相关规则就一直是正确的,这已经是 2 多年前了。)一旦删除了解释器,您就不能再指望执行任何命令或访问其中的任何变量;Tcl 库为您处理状态展开。