c - TclLib C DLL,避免 Tcl_DuplicateObj,调用时出现共享对象错误

标签 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 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 代码如下所示:

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"]"

在更改 C 代码中的字典之前,我必须复制它。否则我会收到“使用共享对象调用”错误。 所以我必须在 Tcl 代码中将返回的字典分配回原来的字典。

我想知道是否有更聪明的方法,允许 C 代码直接在原始字典上工作。 类似于 Tcl 中的“work_on_dict dDict”以及在 C 代码中取消引用参数。

我尝试了很多东西,但没有得出结论,如果这毕竟是可能的。

如果有人能给我提示,我会很高兴,谢谢。


这是解决方案 - 来自 Donal 的回答:

C 代码:

请注意,您应该添加错误检查,可能将搜索范围限制为 TCL_NAMESPACE_ONLY 并调用 Tcl_ObjSetVar2 以获取正确的跟踪作为 Donal 状态。

int work_on_dict_cmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[] )
{
    Tcl_Obj *dReturn = Tcl_ObjGetVar2( interp, objv[1], NULL, 0 );

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

    return TCL_OK;
}

Tcl:

set dDict [dict create]
dict set dDict "key1" 1
work_on_dict dDict                
puts "[dict get $dDict "key1"] [dict get $dDict "key2"]"

最佳答案

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

从 Tcl 修复问题

解决问题的标准方法是让 Tcl_DuplicateObj(dictPtr) 调用以 Tcl_IsShared(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 等的扭曲最终只将引用转移到参数堆栈,删除变量中保存的引用,这样你的命令就会被有效地调用。看起来很疯狂的惊人/恐怖版本实际上也是这样做的,但纯粹是字节码:

% 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 setdict unset 所做的。)因为从 C 端的变量读取(可能使用 Tcl_ObjGetVar2)不会' 更改引用计数,如果该值仅保存在变量中,您将能够直接更新它。您还应该在写入后调用 Tcl_ObjSetVar2,以便触发变量上的任何跟踪。

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

work_on_dict dDict

注意那里没有$;您传递的是变量的名称,而不是内容

关于c - TclLib C DLL,避免 Tcl_DuplicateObj,调用时出现共享对象错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26384619/

相关文章:

java - 中文/日文机器支持 char* 吗?

python - 使用 Tkinter/Tcl 设置窗口样式 : no exit button

c - 安装SIGTSTP前台进程

c - (null) 如何打印?其背后的理念是什么?

c - 制作 : compiling all files once AND generating object files into different directory

c - 如何用 C 中二维数组中的一行初始化变量?

c++ - 如何在 C++ 中使用 dll 文件?

c# - OpenSSL 代码 native 工作,但作为 DLL 导致 OPENSSL_Uplink : no OPENSSL_Applink

exception - 如何更改Tcl错误前缀?

linux - 在绘制 Tcl/Tk 图形之前调整 Y 值