2013-11-04 59 views
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程序的一部分。事实上,它不会显示问题,但我会尽力解释它。Tcl:Interpreter创建跟踪对象的副本,并将其更改为

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

+0

这是一个坏主意。改用手柄。 –

回答

1

首先,TCL的类型系统是要在C中使用非常不同++(及许多其他语言之外)在于:

  1. 类型明确重视价值观,而不是变量。
  2. 值可以在类型之间进行变异。 (这可以通过串行化来完成,以一个字符串,然后解析该字符串,或者它可以通过更有效的机制来实现;细节非常具体的确切例子)

其次,Tcl_RegisterObjType()没有特殊除了Tcl_GetObjType()之外的任何其他API的关系,它在表中查找T_RegisterObjType进入.Tcl本身不会在任何地方调用Tcl_GetObjType;如果注册的类型不是允许的另一个扩展软件包来获得它的类型,那么您从中获益不大。我们也没有记录有哪些类型。并非所有Tcl的内部类型都被注册了 - 这些类型甚至在补丁版本之间都没有保证 - 并且没有公开保证操作的效果对参数类型的影响(尽管有些目前很容易猜到,比如与列表和字典操作)。

由于这些问题,您需要更改您使用的方法。不要直接在数据库中放置数据库句柄,而应使用可读的字符串来查找散列表中的真实句柄。这很容易得到正确的,并且需要更少的棘手编码。唯一的缺点是你最终不得不手动处理手柄;通常你需要执行closeDatabase $handle操作,或者通过在变量上设置一个未设置的跟踪来执行此操作,以便您只需执行unset handle(或者只是对于某个过程,就本地变量而言)来删除。这是a classic approach已经written about a lot,所以我不会在这里详细介绍所有的细节。 (您可能还会发现我很早以前写的this code)。

更复杂的方法是将句柄绑定到TclOO对象。 TclOO C API提供了一种机制,允许您将该句柄注册为实例对象上的一个隐藏的内部值,您可以从TclOO方法(只要它们使用TclOO C API而非脚本)轻松检索该内部值,而您然后从TclOO中使用的生命周期管理代码(定义明确的构造函数和析构函数,包含实体被删除时的回调等)中受益。这是TDBC数据库驱动程序的工作量(例如,tdbc::odbc完全是under the hood)。最后,如果这是一个真正的数据库,那么请使用现有的数据库扩展(建议使用符合TDBC标准的数据库)。为什么?因为那样你就不需要维护大量的代码来完成连接管理;您可以将其全部委托给其他人维护的脚本(易于编写)和扩展。您从C++访问数据库的调用可以变成(自提供的)Tcl命令的调用,可能通过Tcl_EvalObjv,因为这是最有效的公共命令调用功能。