至于第三点我解释如下:
3,至于ns2的两种语言的交互过程网上有几篇帖子介绍的已经非常的详细了,但还是因为高手们把有一些东西省略了,从而对我这种初学者造成了一定的困难,为了以后的“Beginner”们能够更快的捋顺关系,早日入门ns2,我还是要按我的方法说一下。ns2是这么做的,每一个类都有一个Otcl类和一个c++类,每个需要交互的c++类都有一个“链接类(该类包括一个方法,该方法实例化一个指定的类并且返回一个指向该实例的一个指针)”,该“链接类”通过其父类的构造方法在编译阶段注册一个方法到TclLinkage(otcl类可以找到并且可以调用该方法),这样在Otcl类初始化一个一个类的时候,调用之前注册过的方法,就可以初始化一个c++类了,并且还会注册一个使得Otcl类可以调用c++的类的方法——通过顺序查找实现的。这样以后在Otcl类就可以调用c++类中的方法了,说的有点乱,但是如果你看过一个例子,就会觉得我说的有道理了。
还是调用网上某位高手的一个例子吧,在这里我就补充说明一下tcl的unknown机制。
static class ChannelClass : public TclClass {
public:
ChannelClass() : TclClass("Channel") {}
TclObject* create(int, const char*const*) {
return (new Channel);
}
} class_channel
当ns instantiating the class_channel object时,它会invoke ChannelClass:ChannelC
lass();
这会首先invoke TclClass:TclClass("Channel");
在Tcl.cc文件中:
TclClass::TclClass(const char* classname) : class_(0), classname_(classname)
{
#如果Otcl语言解释器已存在的话:
bind();
}
void TclClass::bind()
{
#首先获取Tcl
Tcl& tcl = Tcl::instance();
#在Otcl环境中注册该类名:Channel
#并且该类的父类是SpliteObject
#Note:SpliteObject存在于otcl环境中,与C++中的TclObject相对应
tcl.evalf("SplitObject register %s", classname_);
#注册了之后,为这个类添加两个命令:create-shadow和delete-shadow
#Note:the implementation procedures of these two methods就是
#TclClass::create_shadow()和TclClass::delete_shadow().
class_ = OTclGetClass(tcl.interp(), (char*)classname_);
OTclAddIMethod(class_, "create-shadow",
create_shadow, (ClientData)this, 0);
OTclAddIMethod(class_, "delete-shadow",
delete_shadow, (ClientData)this, 0);
otcl_mappings();
}
然后当你在ns环境中敲入:new Channel时
在文件tcl-object.tcl中:
proc new { className args } {
set o [SplitObject getid]
#调用了该类的create函数,即Channel:create()函数
#也就是调用了SpliteObject:create()函数
if [catch "$className create $o $args" msg] {
if [string match "__FAILED_SHADOW_OBJECT_" $msg] {
#
# The shadow object failed to be allocated.
#
delete $o
return ""
}
global errorInfo
error "class $className: constructor failed: $msg" $errorInfo
}
return $o
}
但是SpliteObject并没有implement create() 函数,
但是你别忘了在otcl环境中,SpliteObject类是这样声明的:Class SpliteObject
所以这会调用Class的Create函数
Class instproc create() {
...
alloc();
init();
...
}
这就会调用SpliteObject instproc init()函数
SplitObject instproc init args {
$self next
#调用类的create-shadow函数
#在这个例子中,就是调用了Channel instproc create_shadow函数
#也就是调用了TclClass::create-shadow()函数,因为在之前的bind()方法中有将这两种操作对应起来。
if [catch "$self create-shadow $args"] {
error "__FAILED_SHADOW_OBJECT_" ""
}
}
int TclClass::create_shadow(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[])
{
TclClass* p = (TclClass*)clientData;
#在这里调用了ChannelClass::create()函数
#也就是调用了C++环境中的:new Channel
#到这里为止,otcl中的Channel的shadow object就生成了
TclObject* o = p->create(argc, argv);
Tcl& tcl = Tcl::instance();
if (o != 0) {
o->name(argv[0]);
tcl.enter(o);
if (o->init(argc - 2, argv + 2) == TCL_ERROR) {
tcl.remove(o);
delete o;
return (TCL_ERROR);
}
tcl.result(o->name());
#在这里再次为otcl中的类Channel添加两个instproc:cmd和instvar
#其中cmd命令是meet the Tcl Unknown mechanism
(如果有同名的tcl方法就调用该方法,如果没有该方法当然是unknown了。
#这样的话,当你在ns脚本中输入了一个该类未知的命令,
#Tcl的unknown机制就会调用该类的cmd命令
#在/tclcl/tcl-object.tcl中
SplitObject instproc unknown args {
if [catch "$self cmd $args" ret] {
set cls [$self info class]
global errorInfo
set savedInfo $errorInfo
error "error when calling class $cls: $args" $savedInfo
}
return $ret
}
#而这进一步的就会调用该类的shadow object的command()过程
#所以在实现类的C++部分时,你必须实现该类的Command()过程
#在command()中实现所有的命令分发
OTclAddPMethod(OTclGetObject(interp, argv[0]), "cmd",
dispatch_cmd, (ClientData)o, 0);
OTclAddPMethod(OTclGetObject(interp, argv[0]), "instvar",
dispatch_instvar, (ClientData)o, 0);
o->delay_bind_init_all();
return (TCL_OK);
} else {
tcl.resultf("new failed while creating object of class %s",
p->classname_);
return (TCL_ERROR);
}
}
通过上面的例子可以了解到 Otcl 可以调用 c++ 类中的方法,而 c++ 中的类可不可以调用 Otcl 类中的方法呢,答案是可以的,是通过得到一个 Tcl 实例来实现的,具体请参考 everything.pdf 里面有详细的介绍(其实我总解的这些也是通过在总结一下学习该文档的经验而已,这个文档真的可以称得上是 everything 了,包括了 ns2 的一切内容,就是太长了)。