0
#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 proc 注册的。问题是,当我在myTraceProcproc 中对跟踪的对象进行更改时,解释器会复制该对象(Tcl_DupInternalRepProc被调用)。这不是程序的期望行为。如果不创建克隆并且所有费用都使用确切的对象完成,那就太好了。我查看了 Tcl_TraceVar文档,但没有找到禁用它的方法。

4

1 回答 1

1

首先,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最有效的公共命令调用函数。

于 2013-11-06T09:18:36.510 回答