如何使用 Critcl 设置参数指向的数据?
How to set data pointed to by argument using Critcl?
我想在Critcl中表达这样的话:
void setter(int* grid, int value, int x, int y) {
grid[xy2addr(x,y)] = value;
}
我特别困惑如何在 Critcl 中处理 int* grid
。 object
? bytes
?也许是自定义类型?
与相关。
这种情况不能很好地映射到 Tcl 的价值模型。问题是 grid
是(指向)可更新值集合的指针。一般来说,在 Tcl 中有两种建模方式:
- 作为不透明对象。
- 作为包含 Tcl 列表的变量(因为在模型方面,虽然 Tcl 值被认为是不可变的,但 Tcl 变量 是可变的)。
我将在下面描述如何执行这两项操作,但我猜您会认为这些 zOrder 事物是一种独特的可变类型,并且制作自定义的额外适度的一次性开销type 会更适合你。
不透明(可变)对象
在处理不透明对象时,您将 句柄 传递给它们(基本上只是一个名称),然后将它们解压缩为 custom Critcl type。诀窍是在 C 中创建一些辅助函数来执行映射(这可以在 critcl::ccode
命令中),在名称和指针之间进行映射。这做起来有点麻烦,但只是建立几个哈希表。
critcl::ccode {
static Tcl_HashTable *zOrderMap = NULL, *zOrderRevMap = NULL;
static Tcl_Obj *
MakeZOrderObj(int *zOrder) {
/* Initialize the two maps, if needed */
if (zOrderMap == NULL) {
zOrderMap = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable));
Tcl_InitObjHashTable(zOrderMap);
zOrderRevMap = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(zOrderRevMap, TCL_ONE_WORD_KEYS);
}
int isNew;
Tcl_HashEntry *hPtr = Tcl_FindHashEntry(zOrderRevMap, (char*) zOrder, &isNew);
if (!isNew) {
return Tcl_GetHashValue(hPtr);
}
/* make a handle! */
Tcl_Obj *handle = Tcl_ObjPrintf("zOrder%ld", (long) zOrder);
Tcl_SetHashValue(hPtr, handle);
Tcl_IncrRefCount(handle);
hPtr = Tcl_CreateHashEntry(zOrderMap, (char*) handle, &isNew);
Tcl_SetHashValue(hPtr, zOrder);
return handle;
}
static int
GetZOrderFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int **zOrderPtr) {
Tcl_HashTable *hPtr;
if (!zOrderMap || (hPtr = Tcl_FindHashEntry(zOrderMap, (char *) objPtr)) == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("no such zOrder \"%s\"",
Tcl_GetString(objPtr)));
return TCL_ERROR;
}
*zOrderPtr = (int *) Tcl_GetHashValue(hPtr);
return TCL_OK;
}
}
有了这个辅助代码,您就可以像这样定义一个自定义的 Critcl 类型:
critcl::argtype zOrder {
if (GetZOrderFromObj(interp, @@, @A) != TCL_OK) {
return TCL_ERROR;
}
} int*
critcl::resulttype zOrder {
if (rv == NULL) {
return TCL_ERROR;
}
Tcl_SetObjResult(interp, MakeZOrderObj(rv));
return TCL_OK;
} int*
这样你就可以像这样编写真正的代码。请注意,grid
被定义为(自定义)类型 zOrder
,并且这些只能由某些代码制造,结果是 returns a zOrder
。
critcl::cproc setter {zOrder grid int value int x int y} void {
grid[xy2addr(x,y)] = value;
}
(从哈希表中删除条目并删除C数组的删除函数留作练习。)
Tcl 列表变量
另一种方法是将 zOrder 值作为整数列表保存在 Tcl 变量中。这可能很好,因为它可以让你轻松地查看内部,但它在其他方面也可能不太好,因为代码没有 constrained 以使用正确的值并且你暴露了你的 cprocs了解 Tcl 中正在发生的事情的更多细节。
critcl::cproc setter {Tcl_Interp* interp object varName int value int x int y} ok {
/* Unpack the list of ints from the variable */
Tcl_Obj *listObj = Tcl_ObjGetVar2(interp, varName, NULL, TCL_LEAVE_ERR_MSG);
if (listObj == NULL)
return TCL_ERROR;
Tcl_Obj **listv; int listc;
if (Tcl_ListObjGetElements(interp, listObj, &listc, &listv) != TCL_OK)
return TCL_ERROR;
int *grid = alloca(sizeof(int) * listc);
for (int i=0; i<listc; i++)
if (Tcl_GetIntFromObj(interp, listv[i], &grid[i]) != TCL_OK)
return TCL_ERROR;
/* The core of the functionality */
grid[xy2addr(x,y)] = value;
/* Repack the list of ints from the variable; this code could be optimized in this case! */
for (int i=0; i<listc; i++)
listv[i] = Tcl_NewIntObj(grid[i]);
listObj = Tcl_NewListObj(listc, listv);
Tcl_ObjSetVar2(interp, varName, NULL, listObj, 0);
return TCL_OK;
}
我想在Critcl中表达这样的话:
void setter(int* grid, int value, int x, int y) {
grid[xy2addr(x,y)] = value;
}
我特别困惑如何在 Critcl 中处理 int* grid
。 object
? bytes
?也许是自定义类型?
与
这种情况不能很好地映射到 Tcl 的价值模型。问题是 grid
是(指向)可更新值集合的指针。一般来说,在 Tcl 中有两种建模方式:
- 作为不透明对象。
- 作为包含 Tcl 列表的变量(因为在模型方面,虽然 Tcl 值被认为是不可变的,但 Tcl 变量 是可变的)。
我将在下面描述如何执行这两项操作,但我猜您会认为这些 zOrder 事物是一种独特的可变类型,并且制作自定义的额外适度的一次性开销type 会更适合你。
不透明(可变)对象
在处理不透明对象时,您将 句柄 传递给它们(基本上只是一个名称),然后将它们解压缩为 custom Critcl type。诀窍是在 C 中创建一些辅助函数来执行映射(这可以在 critcl::ccode
命令中),在名称和指针之间进行映射。这做起来有点麻烦,但只是建立几个哈希表。
critcl::ccode {
static Tcl_HashTable *zOrderMap = NULL, *zOrderRevMap = NULL;
static Tcl_Obj *
MakeZOrderObj(int *zOrder) {
/* Initialize the two maps, if needed */
if (zOrderMap == NULL) {
zOrderMap = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable));
Tcl_InitObjHashTable(zOrderMap);
zOrderRevMap = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(zOrderRevMap, TCL_ONE_WORD_KEYS);
}
int isNew;
Tcl_HashEntry *hPtr = Tcl_FindHashEntry(zOrderRevMap, (char*) zOrder, &isNew);
if (!isNew) {
return Tcl_GetHashValue(hPtr);
}
/* make a handle! */
Tcl_Obj *handle = Tcl_ObjPrintf("zOrder%ld", (long) zOrder);
Tcl_SetHashValue(hPtr, handle);
Tcl_IncrRefCount(handle);
hPtr = Tcl_CreateHashEntry(zOrderMap, (char*) handle, &isNew);
Tcl_SetHashValue(hPtr, zOrder);
return handle;
}
static int
GetZOrderFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int **zOrderPtr) {
Tcl_HashTable *hPtr;
if (!zOrderMap || (hPtr = Tcl_FindHashEntry(zOrderMap, (char *) objPtr)) == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("no such zOrder \"%s\"",
Tcl_GetString(objPtr)));
return TCL_ERROR;
}
*zOrderPtr = (int *) Tcl_GetHashValue(hPtr);
return TCL_OK;
}
}
有了这个辅助代码,您就可以像这样定义一个自定义的 Critcl 类型:
critcl::argtype zOrder {
if (GetZOrderFromObj(interp, @@, @A) != TCL_OK) {
return TCL_ERROR;
}
} int*
critcl::resulttype zOrder {
if (rv == NULL) {
return TCL_ERROR;
}
Tcl_SetObjResult(interp, MakeZOrderObj(rv));
return TCL_OK;
} int*
这样你就可以像这样编写真正的代码。请注意,grid
被定义为(自定义)类型 zOrder
,并且这些只能由某些代码制造,结果是 returns a zOrder
。
critcl::cproc setter {zOrder grid int value int x int y} void {
grid[xy2addr(x,y)] = value;
}
(从哈希表中删除条目并删除C数组的删除函数留作练习。)
Tcl 列表变量
另一种方法是将 zOrder 值作为整数列表保存在 Tcl 变量中。这可能很好,因为它可以让你轻松地查看内部,但它在其他方面也可能不太好,因为代码没有 constrained 以使用正确的值并且你暴露了你的 cprocs了解 Tcl 中正在发生的事情的更多细节。
critcl::cproc setter {Tcl_Interp* interp object varName int value int x int y} ok {
/* Unpack the list of ints from the variable */
Tcl_Obj *listObj = Tcl_ObjGetVar2(interp, varName, NULL, TCL_LEAVE_ERR_MSG);
if (listObj == NULL)
return TCL_ERROR;
Tcl_Obj **listv; int listc;
if (Tcl_ListObjGetElements(interp, listObj, &listc, &listv) != TCL_OK)
return TCL_ERROR;
int *grid = alloca(sizeof(int) * listc);
for (int i=0; i<listc; i++)
if (Tcl_GetIntFromObj(interp, listv[i], &grid[i]) != TCL_OK)
return TCL_ERROR;
/* The core of the functionality */
grid[xy2addr(x,y)] = value;
/* Repack the list of ints from the variable; this code could be optimized in this case! */
for (int i=0; i<listc; i++)
listv[i] = Tcl_NewIntObj(grid[i]);
listObj = Tcl_NewListObj(listc, listv);
Tcl_ObjSetVar2(interp, varName, NULL, listObj, 0);
return TCL_OK;
}