如何使用 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* gridobjectbytes?也许是自定义类型?

相关。

这种情况不能很好地映射到 Tcl 的价值模型。问题是 grid 是(指向)可更新值集合的指针。一般来说,在 Tcl 中有两种建模方式:

  1. 作为不透明对象。
  2. 作为包含 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;
}