如何使用Critcl设置参数所指向的数据?

时间:2019-04-24 10:22:02

标签: tcl

我想在Critcl中表达如下内容:

void setter(int* grid, int value, int x, int y) {
  grid[xy2addr(x,y)] = value;
}

我特别关注如何处理Critcl中的int* gridobjectbytes?可能是自定义类型?

this question有关。

1 个答案:

答案 0 :(得分:2)

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

  1. 作为不透明的对象。
  2. 作为一个包含Tcl列表的变量(因为从模型上讲,虽然Tcl值被认为是不可变的,但Tcl 变量是可变的)。

我将在下面描述如何完成这两项操作,但是我猜想您将把这些zOrder事物视为一种独特的可变类型,并且使定制类型产生的额外适度的一次性开销将会更适合您。

不透明(可变)对象

在处理不透明对象时,将句柄传递给它们(基本上只是一个名称),然后将它们解压缩为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(自定义)类型,并且只能由返回zOrder作为其结果的某些代码来制造。

critcl::cproc setter {zOrder grid int value int x int y} void {
    grid[xy2addr(x,y)] = value;
}

(练习中保留了从哈希表中删除条目并删除C数组的删除功能。)

Tcl列表变量

另一种方法是使zOrder值作为整数列表保存在Tcl变量中。这样做很不错,因为它使您可以轻松地查看内部,但是在其他方面也可能不太理想,因为代码没有受到约束的约束以使用适当的值,并且您将cproc暴露给更多细节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;
}
相关问题