0

我正在使用 C++ 代码扩展我的 Itcl 程序,但遇到了以下问题。

我想使用 TCL-C API 将我的 Itcl 代码中的“引用”返回到我的 C++ 代码中的一个对象,返回值应该注册到我的 Itcl 代码中的一个对象,因此在此之后我可以调用它的方法。我的问题是我真的不知道如何从双方做到这一点。

我看到使用 TCL_LINKVAR(...) API 函数,我可以在这个对象之间创建一个链接到 TCL 中的一个字符串,但我真的不明白如何使用这个 TCL_LINKVAR 函数来处理对象而不是原始类型,例如整数,双精度等...

我将举一个小例子:这是我的 C++ 代码:

Tcl-C API 代码

#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;
}

代码

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++
    }
...
}
4

1 回答 1

2

首先,简单的C 类型之间的变量链接映射——只是数字和char*(Tcl 管理 的内存分配char*)——和 Tcl 变量;它并不真正适合您正在做的事情。原则上你可以对任何结构做同样的事情;链接代码是基本 Tcl 变量跟踪原语 API 的包装。它并不真正适合任何不透明类型或任何具有动态生命周期的东西。

最简单的技术是在 C++ 端保留一个映射(例如 a std::map<std::string,MyObject*>),以便您可以进行按名称查找操作。一旦你有了它,你就可以发明一种简单的命名机制,并将字符串名称保存在 Itcl 对象的 Tcl 变量中。C++ 端的方法由将名称作为参数之一的命令代理,查找相关对象,然后传递其余参数(使用您喜欢的任何其他解析/类型转换),以及 Itcl 方法是在正确位置传递附加名称/句柄的简单包装器(析构函数是相同的,除了代理从地图中删除和delete)。还有其他方法可以做到这一点,但我刚刚描述的方法很容易实现,并且首先要尝试做的事情;它使您无需在设计级别付出大量努力即可对 API 进行原型设计。请注意,对象的字段不能以这种方式很好地映射;它仅适用于方法,在 C++ 中构成良好 API 的因素与在 Tcl/Itcl 中构成良好 API 的因素绝对不同。

您可以通过使用 SWIG 生成绑定来实现类似的目的。它在内部所做的并没有太大的不同,但是它将绑定更多地放在 C++ 中,而不是在 Tcl 方面。Itcl 包装的工作方式会有所不同……</p>

如果您使用的是 Itcl 4,则可以使用 TclOO API(Itcl 4 构建于其之上)从 C++ 完成方法绑定。TclOO 包括用于定义自定义方法和将 C 和 C++ 对象直接附加到其实例的机制;这可以使绑定更加复杂(并允许您在 Tcl 方面进行子类化之类的事情),但正因为如此,它有点复杂。至少从外部看,您最终会得到的 API 与我上面概述的非常相似。

于 2018-08-27T14:06:52.657 回答