как связать между объектами C ++ и объектами itcl - PullRequest
0 голосов
/ 26 августа 2018

Я расширяю свою программу Itcl с помощью кода C ++, и у меня возникла следующая проблема:

Я хочу вернуть "ссылку" в моем коде Itcl на объект в моем коде C ++ с использованием API TCL-C, Возвращаемое значение должно быть зарегистрировано для объекта в моем коде Itcl, чтобы после этого я мог вызывать его методы.Моя проблема в том, что я не знаю, как это сделать с обеих сторон.

Я видел, что с помощью API-функции TCL_LINKVAR (...) я могу создать ссылку между этими объектами на строку в TCL, но я не совсем понимаю, как использовать эту функцию TCL_LINKVAR, когда дело касается объектов ине для примитивных типов, таких как int, double и т.д ...

Я приведу небольшой пример: Это мой код C ++:

Код API Tcl-C

#include <tcl.h>
#include "classA.h"

class MyObject {
    int id;
    ClassA * a;   // classA is defined somewhere else
public:
    MyObject(int iid) : id(iid) {}
    void returnA() { return a; }

};

extern "C" int do_something_command(ClientData     clientData,
                                   Tcl_Interp*    interp,
                                   int            objc,
                                   Tcl_Obj* const objv[])
{
    if (objc < 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "method ?argument ...?");
        return TCL_ERROR;
    }

    MyObject* p = (MyObject*)clientData;
    classA * ret_val = p->returnA();
    if (LINK_VAR.... != TCL_OK) {   // here should be a linkage between ret_val to an Itcl object
        return TCL_ERROR;
    }

    return TCL_OK;
}
extern "C" int test_create(ClientData     clientData,
                              Tcl_Interp*    interp,
                              int            objc,
                              Tcl_Obj* const objv[])
{
    static int obj_count = 0;

    MyObject* p = new MyObject(obj_count);

    char obj_name[13 + TCL_INTEGER_SPACE];
    sprintf(obj_name, "::testobj%d", obj_count++);

    Tcl_CreateObjCommand(interp, "cDoSomething",
                         (Tcl_ObjCmdProc*)do_something_command,
                         (ClientData) p, (Tcl_CmdDeleteProc *) NULL);

    Tcl_SetObjResult(interp, Tcl_NewStringObj(obj_name, strlen(obj_name)));

    return TCL_OK;
}


extern "C" DLLEXPORT int Test_Init(Tcl_Interp *interp)
{
    if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
        return TCL_ERROR;
    }
    if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL) {
        return TCL_ERROR;
    }
    if (Tcl_PkgProvide(interp, "test", "0.1") != TCL_OK) {
        return TCL_ERROR;
    }

    Tcl_CreateObjCommand(interp, "test::create",
                         (Tcl_ObjCmdProc*)test_create,
                         (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL);

    return TCL_OK;
}

Код Itcl

load test.o   // this will be the name of .o file I will create from my C++ code

itcl::class A { 
...
public method doSomething {}
... }

itcl::class B {
   constructor {} {
       set temp [test::create]
       set a [$temp cDoSomething]   // should register a as an classA object
       // here I will call methods that are related to classA object that are registered in C++
    }
...
}

1 Ответ

0 голосов
/ 27 августа 2018

Во-первых, связывание переменных отображает между простыми типами C - только числами и char* (где Tcl управляет выделением памяти для переменных char*) - и переменных Tcl; это не совсем подходит для того, что вы делаете. В принципе, вы можете сделать то же самое с любой структурой; код связывания является оболочкой для базового API-интерфейса трассировки переменных Tcl. Это не очень подходит для любого непрозрачного типа или чего-либо с динамическим временем жизни.

Самым простым способом является сохранение отображения (например, std::map<std::string,MyObject*>) на стороне C ++, чтобы вы могли выполнять операцию поиска по имени. Получив это, вы можете изобрести простой механизм именования и сохранить строковое имя в переменной Tcl в объекте Itcl. Методы на стороне C ++ проксируются командами, которые принимают имя в качестве одного из аргументов, ищут соответствующий объект и передают остальные аргументы (с любым дополнительным синтаксическим анализом / преобразованием типов, которые вы предпочитаете), а также методы Itcl. это тривиальные оболочки, которые передают дополнительное имя / дескриптор в нужном месте (деструктор точно такой же, за исключением того, что используется для удаления из карты и delete). Есть и другие способы сделать это, но тот, который я только что описал, прост в реализации, и сначала попробуйте это сделать; он позволяет создавать прототипы API без значительных усилий на уровне разработки. Обратите внимание, что поля объекта не отображаются таким образом; он предназначен только для методов, и то, что делает для хорошего API в C ++, определенно не то же самое, что то, что делает для хорошего API в Tcl / Itcl.

Вы можете добиться аналогичных результатов, используя SWIG для создания привязки. То, что он делает внутренне, не очень отличается, но в C ++ связывает больше, чем в сторону Tcl. Обтекание Itcl будет работать немного по-другому ...

Если вы используете Itcl 4, привязка метода может быть выполнена из C ++ с использованием API TclOO (поверх которого построен Itcl 4). TclOO включает механизмы для определения пользовательских методов и для непосредственного присоединения объектов C и C ++ к своим экземплярам; это может сделать привязку более сложной (и позволит вам делать такие вещи, как создание подклассов на стороне Tcl), но это немного сложнее из-за этого. И API, который у вас получится, по крайней мере со стороны, будет выглядеть очень похоже на то, что я описал выше.

...