【发布时间】:2021-11-18 18:22:55
【问题描述】:
我无法找到将 c struct 数据类型变量链接到 tcl struct 数据类型变量的方法。 我想将结构变量数据从 c 编程发送到 tcl .. 任何人都可以对此提出建议吗?
【问题讨论】:
我无法找到将 c struct 数据类型变量链接到 tcl struct 数据类型变量的方法。 我想将结构变量数据从 c 编程发送到 tcl .. 任何人都可以对此提出建议吗?
【问题讨论】:
Tcl 没有这样的结构,所以你需要考虑你想要做什么。关键的选择与我们讨论了多少结构以及这些实体需要有多活跃有关。
如果您只是在谈论映射单个结构实例,您可以使用Tcl_LinkVar() 绑定原始C 类型(例如结构的成员),并拥有Tcl 侧变量都是单个数组的元素。这对于系统控制之类的事情来说非常棒。
struct foo_t {
int bar;
double grill;
}
// NEEDS to be at file or global scope!
// Or to something with at least the same lifetime as the Tcl interpreter.
// MUST NOT be stack-allocated or Bad Things Happen™
struct foo_t foo;
// And then to bind the elements in your Init function, and omitting some casts
Tcl_LinkVar(interp, "foo(bar)", &foo.bar, TCL_LINK_INT);
Tcl_LinkVar(interp, "foo(grill)", &foo.grill, TCL_LINK_DOUBLE);
虽然 是 双向绑定,但如果您想绑定其中的许多,它就不会那么好用了。理论上它可以工作,但它真的很乱。
如果您想做相当于将结构体作为值传递,可以使用三种基本方法。这些是将它们映射为列表,将它们映射为 dicts,并执行自定义操作。在所有这些情况下,如果你创建一些辅助函数真的很有帮助,我称之为NewFooObj() 和GetFooFromObj():
对于列表:
Tcl_Obj *NewFooObj(struct foo_t *fooPtr) {
Tcl_Obj *objs[2];
objs[0] = Tcl_NewIntObj(fooPtr->bar);
objs[1] = Tcl_NewDoubleObj(fooPtr->grill);
return Tcl_NewListObj(2, objs);
}
int GetFooFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, struct foo_t *fooPtr) {
int objc;
Tcl_Obj **objv;
if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
return TCL_ERROR;
}
if (objc != 2) {
Tcl_AppendResult(interp, "foo objects need two elements", NULL);
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, objv[0], &fooPtr->bar) != TCL_OK) {
return TCL_ERROR;
}
if (Tcl_GetDoubleFromObj(interp, objv[0], &fooPtr->grill) != TCL_OK) {
return TCL_ERROR;
}
return TCL_OK;
}
对 dicts 做同样的事情是对你如何使用 Tcl_DictObjPut() 和 Tcl_DictObjGet() 的一个练习(但你在阅读时需要小心一点,因为键本身就是引用计数的对象)。
Tcl_Obj *NewFooObj(struct foo_t *fooPtr) {
Tcl_Obj *objPtr = Tcl_NewDictObj();
Tcl_DictObjPut(NULL, objPtr, Tcl_NewStringObj("bar", -1),
Tcl_NewIntObj(fooPtr->bar));
Tcl_DictObjPut(NULL, objPtr, Tcl_NewStringObj("grill", -1),
Tcl_NewDoubleObj(fooPtr->grill));
return objPtr;
}
int GetFooFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, struct foo_t *fooPtr) {
Tcl_Obj *barNameObj = Tcl_NewStringObj("bar", -1);
Tcl_Obj *grillNameObj = Tcl_NewStringObj("grill", -1);
Tcl_Obj *value;
int result = TCL_ERROR;
Tcl_IncrRefCount(barNameObj);
Tcl_IncrRefCount(grillNameObj);
if (Tcl_DictObjGet(interp, objPtr, barNameObj, &value) == TCL_OK
&& Tcl_GetIntFromObj(interp, value, &foo->bar) == TCL_OK
&& Tcl_DictObjGet(interp, objPtr, grillNameObj, &value) == TCL_OK
&& Tcl_GetDoubleFromObj(interp, value, &foo->grill) == TCL_OK) {
result = TCL_OK;
}
Tcl_DecrRefCount(barNameObj);
Tcl_DecrRefCount(grillNameObj);
return result;
}
函数组具有传统的 Tcl 引用计数语义(创建函数提供零引用计数值,读取函数不操作可见引用计数)。
至于第三个选项……是的,你也可以编写你自己的自定义对象类型,它可以使用你想要的任何字符串和内部表示(你可能希望引用堆分配的 struct foo_t 作为内部表示)。如果您走这条路,我们建议您这样做,以便您可以解析纯字符串以获得您的类型(不允许这使它们变得神奇且可能很脆弱)并拥有它,以便您可以始终如一地往返。
如果你想拥有许多实例并且让这些实例是可修改的(并且不仅仅是传递它们;如果只是传递,那么你可以通过 Tcl 代码抛出地址,这在形式上是不安全的但非常方便,并且可以通过句柄化来确保安全,这在许多方面都是活动 Tcl 实体(如命令、变量和通道)的工作方式——将事物转换为句柄使用哈希表在名称和指针之间进行映射)然后你可能有转到 TclOO 或自定义命令系统之类的东西。我认为这个答案已经足够长了,所以再问一次你是否需要这种复杂程度。
【讨论】:
Tcl_LinkVar()。我需要回去重写一些代码......