Tcl:解释器创建跟踪对象的副本,当它被更改时

Tcl: Interpreter creates copy of traced object whet it goes changed

本文关键字:副本 解释器 创建 跟踪 对象 Tcl      更新时间:2023-10-16
#include <tcl.h>
#include <iostream>
using namespace std;
char* myTraceProc(ClientData clientData, Tcl_Interp* interp, const char* name1, const char* name2, int flags) {
    cout << "myTraceProc" << endl;
    //changing the object
    return NULL;
}
int main(int argc, char* argv[]) {
    Tcl_FindExecutable(argv[0]);
    Tcl_Interp *interp = Tcl_CreateInterp(); 
    Tcl_TraceVar(interp, "database", TCL_TRACE_WRITES, myTraceProc, 0);
    return 0;
}

这是我的 c++/tcl 程序的一部分。事实上,它没有显示问题,但我会尝试解释它。

变量database具有自定义类型。它是使用Tcl_RegisterObjType过程注册的。问题是,当我在myTraceProc过程中对跟踪对象进行更改时,解释器会复制该对象(调用Tcl_DupInternalRepProc)。这不是程序的预期行为。如果不创建克隆并且所有费用都使用确切的对象完成,那就太好了。我查看了Tcl_TraceVar文档,但没有找到禁用它的方法。

首先,Tcl的类型系统与C++(以及许多其他语言)中使用的类型系统非常不同,因为:

  1. 类型显式附加到值,而不是变量。
  2. 值可以在类型之间改变。(这可以通过序列化为字符串然后解析该字符串来完成,也可以通过更有效的机制来完成;详细信息非常特定于确切的示例。

其次,Tcl_RegisterObjType()与任何其他 API 没有特殊关系,除了 Tcl_GetObjType() ,它在T_RegisterObjType在其中输入的表中进行查找。Tcl 本身不会在任何地方调用Tcl_GetObjType;注册类型没有任何好处,只能允许另一个扩展包根据需要获取该类型。我们也不会记录有哪些类型。并非所有 Tcl 的内部类型都被注册了——补丁版本之间甚至不能保证类型集——并且没有公开保证操作对参数类型的影响(尽管有些目前很容易猜到,例如列表和字典操作)。

由于这些要点,您需要更改正在使用的方法。不要直接将数据库句柄放在值中,而是放置一个人类可读的字符串,您可以使用该字符串在哈希表中查找实际句柄。这很容易正确,并且需要的棘手编码要少得多。唯一的缺点是您最终不得不手动处理手柄;通常,您可以通过执行closeDatabase $handle操作或在变量上设置未设置跟踪来执行此操作,以便您可以只执行unset handle(或者只是从过程中,如果是局部变量)进行删除。这是一种经典的方法,已经写了很多,所以我不会在这里详细介绍。(您可能还会发现我很久以前编写的这段代码很有趣。

更复杂的方法是将句柄绑定到 TclOO 对象中。TclOO C API 具有一种机制,允许您将句柄注册为实例对象上的隐藏内部值,您可以从 TclOO 方法中轻松检索该值(前提是它们使用 TclOO C API,而不是编写脚本),然后您可以从 TclOO 中使用的生存期管理代码(定义良好的构造函数和析构函数, 删除包含实体时的回调等)。这就是TDBC的数据库驱动程序中有多少工作方式(例如,tdbc::odbc幕下正是这样做的)。

最后,如果这是您正在与之交谈的真实数据库,请使用现有的数据库扩展(建议符合 TDBC)。为什么?因为这样您就不必维护大量代码来进行连接管理;您可以将所有这些委托给其他人维护的脚本(易于编写)和扩展。然后,您从C++访问数据库的调用可以成为(自我提供的)Tcl命令的调用,可能通过Tcl_EvalObjv因为这是最高效的公共命令调用函数。