您的位置:首页 > 其它

new Simulator 到底做了些什么

2014-01-05 09:07 169 查看
转载网址:http://hi.baidu.com/aoxinguy/item/e206041637d450721109b57f

new Simulator 是每个脚本中必须写的一句,而且,也只能写一句(原因见另一贴)

接下来,我们通过代码跟踪(C++和Otcl层次)来看一下,new Simulator到底会做些什么~

以 set ns [new Simulator] 为例

首先 new 属于otcl在tcl-object.tcl 文件中定义的一个全局过程,其定义如下:

proc new { className args } {

set o [SplitObject getid] // getid 只是将SplitObject的静态变量 id值加1, 初始化时为0, 需要注意的是:

getid 中的最后一句,return _o$id,此时返回的实际上就是待创建的OTCl object的唯一ID值,可以看成是引用

if [catch "$className create $o $args" msg] { //此时className是Simulator,o为全局唯一ID值,作为待创建的Simulator实例的参数传入。其作用实际是留在create-shadow中,用来赋值新创建的C++影像对象的name属性(C++对象的父类TclObject有一个属性char* name)

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

}

*****

$className create $o $args 这句话比较关键。 OTCL与C++绑定的巧妙之处就在于 create 是只有OTCL的祖先类 Class 才有的 成员变量 (OTCL语言定义的,写在脚本引擎中去了,查看tcl文件无法找到这个定义;就像很多关键字或者内建命令的定义是写在引擎中一样。tcl文件中的都是拓展定义)!

因此,无论是任何OTCL类,都需要用new 来创建,然后 new 又会调用祖先类的 create 过程

******************************************************************************************************************************

{create过程的定义 在 OTCL参考中是这样定义的:但这只是参考,NS2中可能会对其有所修改,通过tcldebug是无法查看到其中的代码的~~~(因为它是内置的?)

Class instproc create {obj args} {

set h [$self info heritage]

foreach i [concat $self $h] {

if {[$i info commands alloc] != {}} then {

set args [eval [list $i] alloc [list $obj] $args]

$obj class $self

eval [list $obj] init $args

return $obj

}

}

error {No reachable alloc}

}

}

*********************************************************************************************************************************

{附注: 其实,在OTCL中,OTCL层次上实例变量的创建工作非常简单(复杂的是如何创建对应的C++影像实例): 首先,set o [SplitObject getid] 直接获得实例变量的全局唯一ID值(可以理解为地址值~);接下来,所有OTCL层次的创建工作都在 "$className create $o $args" 这句代码中完成。如前所讲,因为create是OTCL语言的内部实现,因此,无法跟踪至其源码 (tcl调试中,从create会直接跳至init); 但,我大概猜测,依据传入的$className
以及$o等参数,OTCL语言本身完成的工作是:将$o 以及$className建立类与实例的关系。 这样,当我们输入 Class info instance命令时,便可以知道$o 指向的OTCL变量是$className类的一个实例了。 从这个意义上来讲,OTCL实例根本不存在创建失败的情况,顶多也就是它对应的C++影像类创建失败了} -----这段话,待证实

在create过程中,会调用init()过程,而这个过程,却是各个OTCL类各自负责具体实现的。

在SplitObject的各个子类的init过程中,总会在最后通过 $self next $args 来调用父类的init过程,依次一直上溯至祖先类SplitObject。因此,SplitObject的init过程是每个OTCL类通过 new 创建时,一定会执行的过程(从子类至父类)。因此,将OTCL与C++绑定机制的具体实现放至这个地方是最合适不过的了。

先引用一下 SplitObject类的init过程定义:

SplitObject instproc init args {

$self next

if [catch "$self create-shadow $args"] {

error "__FAILED_SHADOW_OBJECT_" ""

}

}

由上面可知,这个过程是new 一个 OTCL实例时,最后执行的一步。这里,我暂且不剖析这个过程的执行情况;因为 Simulator类的 init过程是先于上面这段代码执行的,因此,我们先跳至分析Simulator的init过程(定义在ns-lib.tcl中),代码引用如下:

Simulator instproc init args {

# Debojyoti added this for asim

$self instvar useasim_ //定义一些仿真类需要用到的变量,具体意义尚未完全清楚,待以后查明后补上

$self instvar slinks_

$self instvar nconn_

$self instvar sflows_

$self instvar nsflows_

set slinks_(0:0) 0

set nconn_ 0

set conn_ ""

# for short flows stuff

set sflows_ ""

set nsflows_ 0

set useasim_ 0

$self create_packetformat //创建包结构(这又是一个比较大的话题~涉及比较多,需另开贴)

$self use-scheduler Calendar //指定默认的调度器类型

#$self use-scheduler List

$self set nullAgent_ [new Agent/Null] //nullAgent_是要用在整个仿真场景中,任何需要将包进行丢弃处理的地方,实际上就相当于一个垃圾包回收站,只负责将包释放掉~

$self set-address-format def

if {[lindex $args 0] == "-multicast"} { //如果打开多播的话,还需要运行另外的代码进行配置

$self multicast $args

}

eval $self next $args //调用父类的init过程,也就是直接调用SplitObject的init过程,代码见上段

}

Simulator类的init过程中涉及的代码,均已在代码后作了相关注释,就不再多说明。接下来,我们回到先前讲的SplitObejct 的init过程,也就是所有OTCL实例创建时,都要执行的过程;代码再次引用如下:

SplitObject instproc init args {

$self next

if [catch "$self create-shadow $args"] {

error "__FAILED_SHADOW_OBJECT_" ""

}

}

注:1. 此处$args一般传入的是已创建的OTCL实例唯一ID( _o+ interger形式),其用处 前面已有说明

2. catch的作用是,无论 catch中的command是否成功执行,都不会中止;而只会根据结果返回1或0

这里,我们将讲到OTCL与C++绑定或者说是tclcl机制中最关键的一点:

首先,自问:$self create-shadow $args 何以会精确地调用到 Simulator C++类的构造函数呢?(当然,我们知道,通过tclclass机制,实际上直接调用的是SimulatorClass类的create函数,而create函数封装了Simulator C++类的构造函数而矣)

通过跟踪代码,我们知道, 当OTCL执行到 $self create-shadow $args以后($self 的值为 _o4, $args为空),直接跳入的是C++的TclClass类的create_shadow 函数,其代码是:

{至于为什么能够直接跳,我推测:由_o4属于Simulator类,NS框架自然有办法,构造好相应的ClientData 等参数后,调用create_shadow函数}

int TclClass::create_shadow(ClientData clientData, Tcl_Interp *interp,

int argc, CONST84 char *argv[])

{

TclClass* p = (TclClass*)clientData; // tclclass机制

TclObject* o = p->create(argc, argv); // Simulator C++类的构造函数就封装在 p->create(argc,argv)中

Tcl& tcl = Tcl::instance();

if (o != 0) {

o->name(argv[0]); //此处 argv[0]的值为 _o4 (),也就是$self的值

tcl.enter(o);

if (o->init(argc - 2, argv + 2) == TCL_ERROR) { //????待跟踪~ eclipse中跟踪

tcl.remove(o);

delete o;

return (TCL_ERROR);

}

//至此,OTCL对应的C++影像类创建完毕,且OTCL实例与C++影像的绑定关系,也通过o->name(argv[0]); tcl.enter(o) 这两句代码建立起来了。给出_o4,便可以获C++实例o的地址值~

tcl.result(o->name()); // ?

OTclAddPMethod(OTclGetObject(interp, argv[0]), "cmd", //为新创建的OTCL实例新添加cmd过程

(Tcl_CmdProc *) dispatch_cmd, (ClientData)o, 0);

OTclAddPMethod(OTclGetObject(interp, argv[0]), "instvar", //为新创建的OTCL实例新添加instvar过程

(Tcl_CmdProc *) 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);

}

}

对于上述代码,补充说明一下:

一般tclclass类的create函数定义时,都有两个参数,一个是int argc,另一个是const char*const* argv。但,creat函数的具体实现一般都是直接 new Class(); 根本用不到定义的这两个参数。

所以,如果tclclass中的create定义时也不带参数的话,上述代码中的 p->create(argc, argv); 其实完全可以p->create (); 搞不明白,为什么要多添加这两个参数?

当然,用处可能是暂时我们还没发现而矣~留着也无碍 `
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: