如何在C ++对象和itcl对象之间链接

时间:2018-08-26 09:55:04

标签: api tcl itcl

我正在用C ++代码扩展Itcl程序,但是出现了以下问题。

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

我看到,使用TCL_LINKVAR(...)API函数,我可以在此对象之间创建到TCL中的字符串的链接,但是我真的不了解如何使用TCL_LINKVAR函数来处理对象而不是原始对象类型,例如int,double等...

我将举一个小例子: 这是我的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;
}

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 :(得分:2)

首先,简单 C类型(仅数字和char*(其中Tcl管理char*的内存分配)和Tcl变量之间的变量链接映射;它并不真正适合您的工作。原则上,您可以对任何结构执行相同操作;链接代码是对基本Tcl变量跟踪原始API的包装。它实际上不适用于任何不透明类型或具有动态生命周期的任何内容。

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

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

如果您使用的是Itcl 4,则可以使用TclOO API(在Itcl 4的基础上构建)从C ++完成方法绑定。 TclOO包括用于定义自定义方法以及将C和C ++对象直接附加到其实例的机制。这可以使绑定变得更加复杂(并使您可以执行诸如Tcl方面的子类化之类的事情),但由于这个原因,它变得更加复杂。而且,您最终将获得的API至少从外观上类似于我上面概述的外观。