如何在 C++ 对象和 itcl 对象之间 link
how to link between C++ objects and itcl objects
我正在用 C++ 代码扩展我的 Itcl 程序,但我遇到了以下问题。
我想使用 TCL-C API 将我的 Itcl 代码中的 return 一个 "reference" 转换为我的 C++ 代码中的一个对象 API,return 值应该是在我的 Itcl 代码中注册到一个对象,所以在此之后我可以调用它的方法。
我的问题是我真的不知道如何从两方面做到这一点。
我看到使用 TCL_LINKVAR(...) API 函数我可以在这个对象之间创建一个 link 到 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++
}
...
}
首先,简单 C 类型之间的变量链接映射 — 只是数字和 char*
(其中 Tcl 管理 char*
的内存分配)—和 Tcl 变量;它不太适合你正在做的事情。原则上你可以对任何结构做同样的事情;链接代码是基本 Tcl 变量跟踪原语 API 的包装器。它不太适合任何不透明类型或任何具有动态生命周期的类型。
最简单的技术是在 C++ 端保留映射(例如 std::map<std::string,MyObject*>
),以便您可以进行按名称查找操作。一旦你知道了,你就可以发明一个简单的命名机制,并将字符串名称保存在 Itcl 对象的 Tcl 变量中。 C++ 端的方法由将名称作为参数之一的命令代理,查找相关对象,并传递其余参数(使用您喜欢的任何其他 parsing/type-conversion)和 Itcl 方法是在正确位置传递附加 name/handle 的简单包装器(析构函数完全相同,只是代理从映射中删除和 delete
)。还有其他方法可以做到这一点,但我刚才描述的方法很容易实现,而且是首先尝试做的事情;它让您无需在设计层面付出太多努力就可以制作出 API 的原型。请注意,对象 的字段不会 以这种方式很好地映射;它仅用于方法,在 C++ 中构成良好 API 的因素与在 Tcl/Itcl.
中构成良好 API 的因素绝对不同
您可以通过使用 SWIG 生成绑定来实现类似的目的。它在内部所做的事情并没有太大的不同,但是它将绑定更多地放在 C++ 中而不是 Tcl 方面。 Itcl 包装的工作方式略有不同......
如果您使用的是 Itcl 4,则可以使用 TclOO API(Itcl 4 构建于其之上)从 C++ 完成方法绑定。 TclOO 包括定义自定义方法和将 C 和 C++ 对象直接附加到其实例的机制;这可以使绑定更加复杂(并且可以让您执行诸如在 Tcl 方面进行子类化之类的事情)但是因此它有点复杂。 API 你最终得到的外观,至少从外面看,与我上面概述的非常相似。
我正在用 C++ 代码扩展我的 Itcl 程序,但我遇到了以下问题。
我想使用 TCL-C API 将我的 Itcl 代码中的 return 一个 "reference" 转换为我的 C++ 代码中的一个对象 API,return 值应该是在我的 Itcl 代码中注册到一个对象,所以在此之后我可以调用它的方法。 我的问题是我真的不知道如何从两方面做到这一点。
我看到使用 TCL_LINKVAR(...) API 函数我可以在这个对象之间创建一个 link 到 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++
}
...
}
首先,简单 C 类型之间的变量链接映射 — 只是数字和 char*
(其中 Tcl 管理 char*
的内存分配)—和 Tcl 变量;它不太适合你正在做的事情。原则上你可以对任何结构做同样的事情;链接代码是基本 Tcl 变量跟踪原语 API 的包装器。它不太适合任何不透明类型或任何具有动态生命周期的类型。
最简单的技术是在 C++ 端保留映射(例如 std::map<std::string,MyObject*>
),以便您可以进行按名称查找操作。一旦你知道了,你就可以发明一个简单的命名机制,并将字符串名称保存在 Itcl 对象的 Tcl 变量中。 C++ 端的方法由将名称作为参数之一的命令代理,查找相关对象,并传递其余参数(使用您喜欢的任何其他 parsing/type-conversion)和 Itcl 方法是在正确位置传递附加 name/handle 的简单包装器(析构函数完全相同,只是代理从映射中删除和 delete
)。还有其他方法可以做到这一点,但我刚才描述的方法很容易实现,而且是首先尝试做的事情;它让您无需在设计层面付出太多努力就可以制作出 API 的原型。请注意,对象 的字段不会 以这种方式很好地映射;它仅用于方法,在 C++ 中构成良好 API 的因素与在 Tcl/Itcl.
您可以通过使用 SWIG 生成绑定来实现类似的目的。它在内部所做的事情并没有太大的不同,但是它将绑定更多地放在 C++ 中而不是 Tcl 方面。 Itcl 包装的工作方式略有不同......
如果您使用的是 Itcl 4,则可以使用 TclOO API(Itcl 4 构建于其之上)从 C++ 完成方法绑定。 TclOO 包括定义自定义方法和将 C 和 C++ 对象直接附加到其实例的机制;这可以使绑定更加复杂(并且可以让您执行诸如在 Tcl 方面进行子类化之类的事情)但是因此它有点复杂。 API 你最终得到的外观,至少从外面看,与我上面概述的非常相似。