Tcl:解释器创建跟踪对象的副本,它会改变 [英] Tcl: Interpreter creates copy of traced object whet it goes changed

查看:219
本文介绍了Tcl:解释器创建跟踪对象的副本,它会改变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  #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;
//改变对象
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_RegisterObjType 处理进行注册。问题是,当我在跟踪对象在 myTraceProc proc中进行更改时,解释器复制对象( Tcl_DupInternalRepProc 称为)。这不是程序期望的行为。这将是巨大的,如果克隆不创建,所有的收费都完成与确切的对象。我查看了 Tcl_TraceVar 文档,但没有找到方法禁用它。

解决方案

首先,Tcl的类型系统非常不同于C ++中使用的类型系统其他语言除外):


  1. 类型显式附加到值,而不是变量。

  2. 值可以在类型之间变化。 (这可以通过序列化到字符串然后解析该字符串来实现,也可以通过更有效的机制来实现)。

其次, Tcl_RegisterObjType()与任何其他API没有特殊关系,除了 Tcl_GetObjType code>,它在表中查找 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 调用,因为这是最有效的公共命令调用函数。 / p>

#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;
}

This is a part of my c++/tcl program. In fact it doesn't show the problem but I'll try to explain it.

The variable database has custom type. It is registrated using Tcl_RegisterObjType proc. The problem is that when I do a change with traced object in the myTraceProc proc, interpreter duplicates the object (Tcl_DupInternalRepProc is called). This is not desired behavior of the program. It would be great if clone doesn't be created and all charges be done with the exact object. I have looked Tcl_TraceVar documentation but didn't find an way to disable it.

解决方案

First off, Tcl's type system is very different to that used in C++ (and many other languages besides) in that:

  1. Types attach explicitly to values, not to variables.
  2. Values can be mutated between types. (This can be done by serialisation to a string and then parsing that string, or it can be done via a more efficient mechanism; the details are very specific to the exact example.)

Secondly, Tcl_RegisterObjType() has no special relationship to any other API except for Tcl_GetObjType(), which does the lookup in the table that T_RegisterObjType makes an entry in. Tcl itself does not call Tcl_GetObjType anywhere; you gain no advantage from registering the type other than to allow another extension package to get hold of the type if it wishes. We also don't document what types there are. Not all of Tcl's internal types are registered — the set of types is not even guaranteed between patch versions — and there's no public guarantee of what the effect of operations are on the types of arguments (though some are currently pretty easy to guess, such as with list and dictionary operations).

Because of these points, you need to change the approach you are using. Instead of putting a database handle directly in the value, instead put a human-readable string that you can use to look up the real handle in a hash table. This is pretty easy to get right, and requires significantly less tricky coding. The only downside is that you end up having to use manual disposal of the handle; typically you'd do this by having a closeDatabase $handle operation, or by setting an unset trace on a variable such that you can just do unset handle (or just to from a procedure, in the case of a local variable) to have the deletion happen. This is a classic approach that has been written about a lot, so I won't go into all the details here. (You might also find this code interesting which I wrote rather a long time ago.)

A more sophisticated approach is to bind the handle into a TclOO object. The TclOO C API has a mechanism to allow you to register the handle as a hidden internal value on the instance object which you can retrieve easily from your TclOO methods (provided they're using the TclOO C API, instead of being scripted) and you then get to benefit from the lifetime management code used in TclOO (well-defined constructors and destructors, callbacks when the containing entity is deleted, etc.). This is how many of TDBC's database drivers work (e.g., tdbc::odbc does exactly this under the hood).

Finally, if this is a real database you're talking to, use an existing database extension (TDBC-compliant is recommended). Why? Because then you're not having to maintain a large body of code to do the connection management; you can delegate that all to scripts (easy to write) and extensions maintained by other people. Your calls to access the database from C++ can then just become invocations of (self-provided) Tcl commands, probably via Tcl_EvalObjv as that is the most efficient public command invocation function.

这篇关于Tcl:解释器创建跟踪对象的副本,它会改变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆