TclLib C DLL,避免使用共享对象错误调用Tcl_DuplicateObj

TclLib C DLL,避免使用共享对象错误调用Tcl_DuplicateObj,c,dll,tcl,duplicate-data,C,Dll,Tcl,Duplicate Data,我正在编写一个创建新命令的C DLL: Tcl_CreateObjCommand( interp, "work_on_dict", (Tcl_ObjCmdProc *)work_on_dict_cmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL ); 该命令的实现是: int work_on_dict_cmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const o

我正在编写一个创建新命令的C DLL:

Tcl_CreateObjCommand( interp, "work_on_dict", (Tcl_ObjCmdProc *)work_on_dict_cmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL );
该命令的实现是:

int work_on_dict_cmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] )
{
    Tcl_Obj *dReturn = Tcl_DuplicateObj( objv[1] );    <------------------- duplicate

    Tcl_DictObjPut( interp, dReturn, Tcl_NewStringObj("key2", -1), Tcl_NewIntObj(2) );
    Tcl_SetObjResult( interp, dReturn );

    return TCL_OK;
}
Tcl:


在调用命令的方式中,至少有两个对字典的引用,一个来自保存字典的变量,另一个来自调用期间的参数堆栈。当然,可能还有其他人

从Tcl解决问题 解决问题的标准方法是让
Tcl\u DuplicateObj(dictPtr)
调用以
Tcl\u ishared(dictPtr)
是否返回true为条件。这样,您就可以像这样调用代码以实现高效工作:

set dDict [work_on_dict $dDict[set dDict {}]]
是的,太难看了。如果我们使用这个使用“著名”组合符K的旧版本,就更容易理解了

proc K {x y} {return $x}
set dDict [work_on_dict [K $dDict [set dDict anyOldConstantValue]]]
使用
K
etc进行的扭曲最终只会将引用转移到参数堆栈,删除变量中的引用,以便有效地调用命令。看起来很疯狂的惊人/恐怖版本实际上也做了同样的事情,但纯粹是字节码:

% tcl::unsupported::disassemble script {set dDict [work_on_dict $dDict[set dDict {}]]}
ByteCode 0x0x1008aee10, refCt 1, epoch 15, interp 0x0x100829a10 (epoch 15)
  Source "set dDict [work_on_dict $dDict[set dDict {}]]"
  Cmds 3, src 45, inst 27, litObjs 3, aux 0, stkDepth 5, code/src 0.00
  Commands 3:
      1: pc 0-25, src 0-44        2: pc 2-24, src 11-43
      3: pc 7-20, src 31-42
  Command 1: "set dDict [work_on_dict $dDict[set dDict {}]]"
    (0) push1 0     # "dDict"
  Command 2: "work_on_dict $dDict[set dDict {}]"
    (2) push1 1     # "work_on_dict"
    (4) push1 0     # "dDict"
    (6) loadStk 
  Command 3: "set dDict {}"
    (7) startCommand +14 1  # next cmd at pc 21
    (16) push1 0    # "dDict"
    (18) push1 2    # ""
    (20) storeStk 
    (21) concat1 2 
    (23) invokeStk1 2 
    (25) storeStk 
    (26) done 
concat1
操作码有一个优化,这意味着如果第二个字符串是空字符串文字,它什么也不做

从C中修复问题-我实际上推荐的 通常进行字典(或列表)修改的命令通常采用包含dict的变量的名称(例如,
dict set
dict unset
do)。因为从C端的变量读取(可能使用
Tcl_ObjGetVar2
)不会更改引用计数,如果该值仅保存在变量中,则可以直接更新它。您还应该在写入之后调用
Tcl_ObjSetVar2
,以便触发变量上的任何跟踪

这样,您就可以将命令更改为这样调用:

set dDict [dict create]
dict set dDict "key1" 1
set dDict [work_on_dict $dDict]                         <------------------- assign back
puts "[dict get $dDict "key1"] [dict get $dDict "key2"]"
work_on_dict dDict

注意这里没有
$
;您传入的是变量的名称,而不是其内容。

您好,多纳尔,谢谢您的详细回答。是的,当使用调试器查看Tcl_Obj时,我可以看到这两个引用;-)在Tcl玩这个把戏时发生了一些奇怪的事情。我将尝试更改我的C代码。我在上面的问题中发布了解决方案的代码。它起作用了。非常感谢你的帮助!
% tcl::unsupported::disassemble script {set dDict [work_on_dict $dDict[set dDict {}]]}
ByteCode 0x0x1008aee10, refCt 1, epoch 15, interp 0x0x100829a10 (epoch 15)
  Source "set dDict [work_on_dict $dDict[set dDict {}]]"
  Cmds 3, src 45, inst 27, litObjs 3, aux 0, stkDepth 5, code/src 0.00
  Commands 3:
      1: pc 0-25, src 0-44        2: pc 2-24, src 11-43
      3: pc 7-20, src 31-42
  Command 1: "set dDict [work_on_dict $dDict[set dDict {}]]"
    (0) push1 0     # "dDict"
  Command 2: "work_on_dict $dDict[set dDict {}]"
    (2) push1 1     # "work_on_dict"
    (4) push1 0     # "dDict"
    (6) loadStk 
  Command 3: "set dDict {}"
    (7) startCommand +14 1  # next cmd at pc 21
    (16) push1 0    # "dDict"
    (18) push1 2    # ""
    (20) storeStk 
    (21) concat1 2 
    (23) invokeStk1 2 
    (25) storeStk 
    (26) done 
work_on_dict dDict