c - 从自定义命令调用其他 Tcl 命令 (Tcl_CmdProc)



乍一看(见下面的证据),看起来虽然Tcl_CmdProc有控制权,但口译员正在等待它返回,在此期间无法接受任何其他呼叫。

那么,如何在返回之前对 Tcl 进行任何调用,例如用户定义的函数?我想我可能需要在解释器或其他东西中设置一个新的调用堆栈帧(稍后展开它)。Tcl_CreateCommand手册页对此事只字未提。

大局是这样的:

我正在修复 https://bugs.python.org/issue33257.TkinterHandlers.py 示例使用 Python 事件处理程序,这些处理程序在后台作为自定义 Tcl 命令实现。目前,他们的实现在执行Python代码时释放了"Tcl锁"(一个特定于Python的锁,它包装了所有Tcl调用),并在最后重新获取它以Tcl_SetObjResult- 从而允许其他调用同一解释器同时

。现在,如果在此期间实际对解释器进行了另一次调用,Tcl 很快就会abortstderr上显示一条消息:TclStackFree: incorrect freePtr. Call out of sequence?

如果我让自定义命令保留 Tcl 锁,它稍后会冻结尝试再次获取锁,因为它本身有时也需要进行 Tcl 调用。现在,我可以使锁可重入,但是如果不知道如何正确处理解释器,我可能也会破坏它。


为了保持这个问题的主题,我特别询问如何处理口译员,特别是从Tcl_CmdProc进行Tcl调用。具体情况仅用于说明我的需求。如果这实际上在我找不到的某个文档中得到了解释,那么链接到它并背诵一些关键点就足够了。

要从 C 代码调用 Tcl 命令,您可以在两个 API 函数系列之间进行选择。一个是Tcl_EvalObjv,一个是Tcl_Eval。每个都有许多变体,但我要提到的唯一变体是Tcl_EvalObjEx.

Tcl_EvalObjv

此函数调用单个 Tcl 命令,处理参数中的替换(当然,除非命令本身执行这些替换)。它具有以下签名:

int Tcl_EvalObjv(Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[],
int flags);

它采用描述要调用的命令以及要传递给它的参数作为 Tcl 值引用的 C 数组(在参数objv中),其中数组的长度为objc;Tcl 保证不修改数组本身,但如果它进行类型转换,可能会转换值。这些值都必须具有非零引用计数(并且所有值都从其出生Tcl_NewObj调用开始以零引用计数开头)。interp是解释器上下文,flags通常可以为零。

结果是一个Tcl异常代码;如果它是TCL_OK,调用的结果可以使用Tcl_GetObjResult从解释器中检索,如果异常代码TCL_ERROR则存在错误,您通常应该将其传递出去(也许使用Tcl_AddErrorInfo添加到堆栈跟踪中)。其他异常代码是可能的;通常最好直接传递这些内容而不进行任何进一步的处理(除非您正在制作类似循环的东西,此时您应该注意TCL_BREAKTCL_CONTINUE)。

Tcl_Eval

此函数评估 Tcl 脚本,而不仅仅是单个命令,其中包括处理参数中的替换。它具有以下签名:

int Tcl_Eval(Tcl_Interp *interp,
const char *script);

script是任何旧的 C 字符串;Tcl 不会修改它,但它会解析、字节码编译并执行它。由您来提供脚本的形式,该形式将毫无意外地执行单个命令。interp参数和函数调用的结果与Tcl_EvalObjv相同。

如果您有兴趣使用它来运行单个命令,实际上最好使用Tcl_EvalObjv或......

Tcl_EvalObjEx.

这就像Tcl_Eval,只是它将脚本作为 Tcl 值引用(并且也需要flags)。

int Tcl_EvalObjEx(Tcl_Interp *interp,
Tcl_Obj *objPtr,
int flags);

同样,请确保objPtr具有非零引用计数,然后再将其传递到此函数中。(它可以在执行期间调整引用计数。同样,interp和结果如Tcl_EvalObjv记录的那样,flags也是如此。

调用单个命令的优点是,您可以调用Tcl_NewListObj(或任何其他列表构建函数)来使脚本值;这样做可以保证不会有意外的替换。但是您也可以直接使用Tcl_EvalObjv调用命令。但是,如果你想处理比对命令的单个简单调用更复杂的任何内容,这是一个很好的起点,因为它有一个普通Tcl_Eval没有的关键优势:它可以使通过传入的脚本类型成为缓存编译字节码的类型objPtr在某些情况下允许相当合理的性能提升。

请注意,Tcl_EvalObjv实际上是 Tcl 在内部调用以调用所有用户代码并执行所有I/O 的 API。 ("有效"是因为在 Tcl 8.6 中事情变得更加复杂。


在一个Tcl_CmdProc内,所有这些函数都可以像往常一样调用,不需要特殊的处理或"解释器的处理"。如果这对你不起作用,导致崩溃或其他什么,解释器没有错,你的代码一定有其他问题。

最新更新