Как установить данные, на которые указывает аргумент, используя Critcl? - PullRequest
1 голос
/ 24 апреля 2019

Я хотел бы выразить что-то подобное в Critcl :

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

В частности, я застрял в том, как иметь дело с int* grid в Critcl.object?bytes?Пользовательский тип может быть?

В связи с этот вопрос .

1 Ответ

2 голосов
/ 24 апреля 2019

Этот случай не очень хорошо отображается на модель стоимости Tcl. Проблема в том, что grid (указатель на) обновляемый набор значений. Есть два способа моделирования этого в Tcl в целом:

  1. как непрозрачный объект.
  2. В качестве переменной, содержащей список 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...