我有一个小的shell应用程序,它嵌入了Tcl 8.4来执行一些Tcl代码。使用Tcl_CreateInterp
初始化Tcl解释器。一切都很简单:
Tcl_Eval
进行评估Tcl_Eval
命令?我可以处理'Ctrl + C'信号,但是如何中断Tcl_Eval
?
答案 0 :(得分:1)
默认情况下,Tcl不会设置信号处理程序(SIGPIPE除外,您可能根本不关心它),因此您需要使用该语言的扩展来获得所需的功能。
到目前为止,最简单的方法是使用TclX package中的signal
命令(或来自Expect包,但在其他方面更具侵入性):
package require Tclx
# Make Ctrl+C generate an error
signal error SIGINT
在使用Tcl_Eval()
开始运行您希望能够中断的代码之前,只需评估包含同一解释器中的脚本的脚本; a Ctrl + C 会导致Tcl_Eval()
返回TCL_ERROR
。 (有other things you can do - 例如运行任意Tcl命令,它可以回收到你的C代码 - 但这是最简单的。)
如果你在Windows上,显然是TWAPI package can do something equivalent。
以下是交互式会话中的演示!
bash$ tclsh8.6 % package require Tclx 8.4 % signal error SIGINT % puts [list [catch { while 1 {incr i} } a b] $a $b $errorInfo $errorCode] ^C1 {can't read "i": no such variableSIGINT signal received} {-code 1 -level 0 -errorstack {INNER push1} -errorcode {POSIX SIG SIGINT} -errorinfo {can't read "i": no such variableSIGINT signal received while executing "incr i"} -errorline 2} {can't read "i": no such variableSIGINT signal received while executing "incr i"} {POSIX SIG SIGINT} %
另请注意,这可能会使解释器处于奇怪的状态;错误信息有点奇怪(事实上,这将是一个错误,但我不知道是什么)。像这样(在8.6中)这样做可能更优雅:
% try {
while 1 {incr i}
} trap {POSIX SIG SIGINT} -> {
puts "interrupt"
}
^Cinterrupt
%
答案 1 :(得分:0)
解决这个问题的另一种方法是将你的tcl解释器分成一个单独的进程,并从你的主进程驱动tcl解释器的stdin和stdout。然后,在主进程中,您可以拦截Ctrl-C并使用它来终止分叉tcl解释器的进程并重新生成新的tcl解释器。
使用此解决方案,tcl解释器将永远不会锁定您的主程序。但是,如果需要在主进程中运行它们,那么添加c-function扩展非常烦人,因为你需要使用进程间通信来调用函数。
我有一个类似的问题,我试图解决,我在工作线程中启动TCL解释。除此之外,实际上没有杀死工作线程的干净方法,因为它将分配的内存保留在未清除的状态,导致内存泄漏。因此,解决此问题的唯一方法是使用流程模型,或者只是继续退出并重新启动应用程序。考虑到使用流程解决方案所需的时间,我决定坚持使用线程并解决问题,以便让ctrl-c在单独的进程中工作,而不是每次杀死线程时泄漏内存。和潜在的破坏稳定和崩溃我的计划。
更新:
我的结论是Tcl Arrays不是正常变量,你不能使用Tcl_GetVar2Ex在Eval之后读取“tmp”变量,并且tmp不会出现在“info globals”之下。因此,为了解决这个问题,我决定直接调用Tcl-Library API而不是Eval快捷方式来构建要返回的字典对象。
Tcl_Obj* dict_obj = Tcl_NewDictObj ();
if (!dict_obj) {
return TCL_ERROR;
}
Tcl_DictObjPut (
interp,
dict_obj,
Tcl_NewStringObj ("key1", -1),
Tcl_NewStringObj ("value1", -1)
);
Tcl_DictObjPut (
interp,
dict_obj,
Tcl_NewStringObj ("key2", -1),
Tcl_NewStringObj ("value2", -1)
);
Tcl_SetObjResult(interp, dict_obj);