Tcl 扩展:理解和使用 ClientData
Tcl extension: Understanding and using ClientData
我正在努力更新旧软件(针对 Tcl/Tk 8.3 编写),以便它针对更现代的发行版构建。我借此机会学习 C 和 Tcl,在这个项目开始时我从未编程过这两种语言。所以,我是新手。
我在学习 Tcl API 时最困惑的事情之一是 ClientData
。 API 中的许多函数都将它作为参数,但 C 扩展几乎从不使用它。我从 this answer 得到的印象是很少使用全局定义:
The whole-extension ClientData is intended for extensions that want to
publish their own stub table ... that other extensions can build against.
This is a very rare thing to want to do; leave at NULL if you don't
want it.
所以它似乎基本上是一个 void
参数,然后您需要在内部键入 cast 或 handle。但是代码中有一些地方需要和ClientData交互(比如Idle Processes,如下):
typedef struct graph *Graph;
struct graph {
int num;
int hidden;
int needsRedraw;
...
};
flag drawNow(ClientData data) {
Graph G;
int g = (int) data;
if (!(G = lookupGraph(g))) return TCL_OK;
return drawGraph(G);
}
flag drawLater(Graph G) {
if (!G->needsRedraw && !G->hidden) {
G->needsRedraw = TRUE;
Tcl_DoWhenIdle((Tcl_IdleProc *) drawNow, (ClientData) G->num);
}
return TCL_OK;
}
当我编译时转换为整数导致以下警告:
warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
我的主要困惑是(ClientData) G->num
---我不明白我要投射到什么,因为我不知道ClientData 是什么东西。如果我查看 Tcl 源代码,我发现不清楚:
tclCompile.h:220:typedef ClientData (AuxDataDupProc) (ClientData clientData);
tclInt.h:2520:typedef ClientData (TclFSGetCwdProc2)(ClientData clientData);
tclOOInt.h:73:typedef ClientData (TclOO_PmCDCloneProc)(ClientData clientData);
最后几点:这些编译器警告在我开始对代码进行任何更新之前就已经存在。该程序似乎可以在原始代码编译和 运行 的旧系统上运行。所以,我不确定这个警告是否有任何意义……但它吓坏了我,只要我花时间研究代码并学习 C 和 Tcl,我就希望能查个水落石出。
如果我没有提供足够的相关信息,请提前致歉。我很乐意提供更多,但这是我目前在一个好问题上的最大努力。
一般来说,ClientData
表示“Tcl 承诺 它不会为您解释的指针”,并且几乎总是会返回给您的东西在回调中。像 void *
一样对待它(这就是 typedef
的目的)。
您提到的答案是专门讨论 Tcl_PkgRequireEx
函数,其中 ClientData
内容用于传递对存根表的引用。你可以用它来传递你自己的,但它非常复杂。不过,这是一件非常罕见的事情;大多数人从未触及这些谜团。
一般来说,它更常用于 Tcl_CreateCommand
或 Tcl_CreateObjCommand
。在那里,clientData
参数是一个指针,当创建的命令被调用时,它将被传递回实现回调;对于代表 类 和类似事物的实例的命令,这是 很棒 。 Tcl 将其用于诸如过程之类的事情; proc
命令使用 clientData
将指向描述主体脚本(和其他内容)的结构的指针传递给实现过程的 C 函数。如果您对 Tk 有所了解,clientData
在那里被广泛使用以将低级句柄传递给 window 结构。
对于Tcl_DoWhenIdle
,它又是一个指针被移交给回调。当然,回调函数的签名不同,但基本的基本思想相同。
如果 Tcl 提供 C++ APIs,它可能会做一些不同的事情,因为可以用该语言讨论指向实例方法的指针。但是 Tcl 的 API 是一个 C API,所以一切都必须像这样工作。
我正在努力更新旧软件(针对 Tcl/Tk 8.3 编写),以便它针对更现代的发行版构建。我借此机会学习 C 和 Tcl,在这个项目开始时我从未编程过这两种语言。所以,我是新手。
我在学习 Tcl API 时最困惑的事情之一是 ClientData
。 API 中的许多函数都将它作为参数,但 C 扩展几乎从不使用它。我从 this answer 得到的印象是很少使用全局定义:
The whole-extension ClientData is intended for extensions that want to publish their own stub table ... that other extensions can build against. This is a very rare thing to want to do; leave at NULL if you don't want it.
所以它似乎基本上是一个 void
参数,然后您需要在内部键入 cast 或 handle。但是代码中有一些地方需要和ClientData交互(比如Idle Processes,如下):
typedef struct graph *Graph;
struct graph {
int num;
int hidden;
int needsRedraw;
...
};
flag drawNow(ClientData data) {
Graph G;
int g = (int) data;
if (!(G = lookupGraph(g))) return TCL_OK;
return drawGraph(G);
}
flag drawLater(Graph G) {
if (!G->needsRedraw && !G->hidden) {
G->needsRedraw = TRUE;
Tcl_DoWhenIdle((Tcl_IdleProc *) drawNow, (ClientData) G->num);
}
return TCL_OK;
}
当我编译时转换为整数导致以下警告:
warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
我的主要困惑是(ClientData) G->num
---我不明白我要投射到什么,因为我不知道ClientData 是什么东西。如果我查看 Tcl 源代码,我发现不清楚:
tclCompile.h:220:typedef ClientData (AuxDataDupProc) (ClientData clientData);
tclInt.h:2520:typedef ClientData (TclFSGetCwdProc2)(ClientData clientData);
tclOOInt.h:73:typedef ClientData (TclOO_PmCDCloneProc)(ClientData clientData);
最后几点:这些编译器警告在我开始对代码进行任何更新之前就已经存在。该程序似乎可以在原始代码编译和 运行 的旧系统上运行。所以,我不确定这个警告是否有任何意义……但它吓坏了我,只要我花时间研究代码并学习 C 和 Tcl,我就希望能查个水落石出。
如果我没有提供足够的相关信息,请提前致歉。我很乐意提供更多,但这是我目前在一个好问题上的最大努力。
一般来说,ClientData
表示“Tcl 承诺 它不会为您解释的指针”,并且几乎总是会返回给您的东西在回调中。像 void *
一样对待它(这就是 typedef
的目的)。
您提到的答案是专门讨论 Tcl_PkgRequireEx
函数,其中 ClientData
内容用于传递对存根表的引用。你可以用它来传递你自己的,但它非常复杂。不过,这是一件非常罕见的事情;大多数人从未触及这些谜团。
一般来说,它更常用于 Tcl_CreateCommand
或 Tcl_CreateObjCommand
。在那里,clientData
参数是一个指针,当创建的命令被调用时,它将被传递回实现回调;对于代表 类 和类似事物的实例的命令,这是 很棒 。 Tcl 将其用于诸如过程之类的事情; proc
命令使用 clientData
将指向描述主体脚本(和其他内容)的结构的指针传递给实现过程的 C 函数。如果您对 Tk 有所了解,clientData
在那里被广泛使用以将低级句柄传递给 window 结构。
对于Tcl_DoWhenIdle
,它又是一个指针被移交给回调。当然,回调函数的签名不同,但基本的基本思想相同。
如果 Tcl 提供 C++ APIs,它可能会做一些不同的事情,因为可以用该语言讨论指向实例方法的指针。但是 Tcl 的 API 是一个 C API,所以一切都必须像这样工作。