Этот случай не очень хорошо отображается на модель стоимости Tcl. Проблема в том, что grid
(указатель на) обновляемый набор значений. Есть два способа моделирования этого в Tcl в целом:
- как непрозрачный объект.
- В качестве переменной, содержащей список Tcl (поскольку в модельных терминах значения Tcl считаются неизменяемыми, переменные Tcl являются изменяемыми).
Я опишу, как сделать оба ниже, но я предполагаю, что вы будете думать об этих вещах zOrder как об отдельном изменяемом типе, и что дополнительные скромные одноразовые накладные расходы на создание пользовательского типа будут подходит вам гораздо лучше.
Непрозрачные (изменяемые) объекты
При работе с непрозрачными объектами вы передаете им маркеры (в основном просто имя), а затем распаковываете их как пользовательский тип Critcl . Хитрость заключается в том, чтобы создать некоторые вспомогательные функции в 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 в виде списков целых чисел. Это может быть хорошо, потому что позволяет легко заглядывать внутрь, но может быть и не так приятно в других отношениях, так как код не ограничен для работы с правильными значениями, и вы предоставляете своим 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;
}