NS2变量绑定之bind()

NS2支持C++变量和Tcl变量的双向绑定,这样,能通过它们访问同一个数据,而对其中一个变量做出了修改,与之绑定的变量也被修改.

 

NS2支持5种不同的类型:实数(reals)、整数(integers)、时间(timevalued variables)、带宽(bandwidthvalued variables)、布尔(booleans).

 

如下说明了5中类型的变量的绑定方法:



函数中,参数1指定了Tcl变量的名称,参数2指定了对应的C++变量的地址。

 

下面,我们跟踪bind函数,来大致了解其工作原理。

 

tcl/Tcl.cc文件中,有这样的一段代码,实际上是定义了9个函数

#defineTOB(FUNCTION, C_TYPE, INSTVAR_TYPE, OTHER_STUFF) \

voidTclObject::FUNCTION(const char* var, C_TYPE* val) \

{\

  create_instvar(var); \

  OTHER_STUFF; \

                        init(new INSTVAR_TYPE(var, val), var); \

}

 

 

TOB(bind, double,InstVarReal, ;)

TOB(bind_bw, double,InstVarBandwidth, ;)

TOB(bind_time,double, InstVarTime, ;)

TOB(bind, int,InstVarInt, ;)

TOB(bind, unsignedint, InstVarUInt, ;)

TOB(bind_bool, int,InstVarBool, ;)

TOB(bind,TclObject*, InstVarTclObject, ;)

TOB(bind, TracedInt,InstVarTracedInt, val->name(var); val->owner(this);)

TOB(bind,TracedDouble, InstVarTracedReal, val->name(var); val->owner(this);)

 

其中InstVar***都是class InstVar的派生类,例如InstVarReal

class InstVarReal :public InstVar {

public:

  InstVarReal(const char* name, double* val)

:InstVar(name), val_(val) {}

constchar* snget(char *wrk, int wrklen) {

if(-1 == snprintf(wrk, wrklen, "%.17g", *val_))

abort();

return(wrk);

}

voidset(const char* s) {

*val_= atof(s);

}

 protected:

double*val_;

};

 

继续看bind函数调用的create_instvar(),定义如下

void

TclObject::create_instvar(constchar* var)

{

/*

 * XXX can't use tcl.evalf() because it usesTcl_GlobalEval

 * and we need to run in the context of themethod.

 */

charwrk[256];

sprintf(wrk,"$self instvar %s", var);

Tcl_Eval(Tcl::instance().interp(),wrk);

}

 

注意TclObject类是所有服从分裂对象模型的C++类的基类。可见,该函数执行了Tcl命令:$self instvar name

需要说明的是,instvar{}最初是在otcl_Init()函数(otcl/otcl.c)中被添加的,如下

OTclAddIMethod(theobj,"instvar", (Tcl_CmdProc *) OTclOInstVarMethod, 0, 0);

 

PS:TclClass::create_shadow()函数(在创建OTcl对象时被调用)中,也添加了instvar{}

OTclAddPMethod(OTclGetObject(interp,argv[0]), "instvar",

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

而且,dispatch_instvar()部分代码跟OTclOInstVarMethod()一样的.但是,该添加操作是在构造TclObject对象之后发生的(create_shadow()调用create()构造了该对象,OTclAddPMethod()create()之后被调用),而对bind()的调用发生在构造函数中,所以第一次构造TclObject对象时,用的是otcl_Init()添加的OTclOInstVarMethod()。(此处还需验证)

 

继续看bind()函数中的init(new INSTVAR_TYPE(var, val), var);

首先new了一个新的InstVar***对象,然后调用TclObject::init()

voidTclObject::init(InstVar* v, const char* var)

{

insert(v);

v->init(var);

}

 

每个TclObject对象维护了一个instvar_链表,记录绑定的变量的信息.

再来看InstVar::init()

voidInstVar::init(const char* var)

{

charwrk[256];

sprintf(wrk,"$self init-instvar %s", var);

if(Tcl_Eval(Tcl::instance().interp(), wrk) != TCL_OK) {

/*XXXcan only happy if TclObject::init-instvar broken */

Tcl::instance().evalf("putsstderr \"init-instvar: $errorInfo\"");

exit(1);

}

}

函数运行Tcl命令 $selfinit-instvar var来初始化变量

 

init-instvar{}定义在tclcl/embedded-tclobj.cc

SplitObjectinstproc init-instvar var {

set cl [$self info class]

while { \"$cl\" != \"\" &&\"$cl\" != \"SplitObject\" } {

foreach c $cl {

if ![catch \"$c set $var\" val] {

$self set $var $val

return

}

}

set parents \"\"

foreach c $cl {

if { $cl != \"SplitObject\" && $cl !=\"Object\" } {

set parents \"$parents [$c info superclass]\"

}

}

set cl $parents

}

$self warn-instvar [$self info class]::$var

}

该过检查对象和其所有的父类,找到第一个定义var变量的类,使用该类的变量的值初始化var,大部分的这样的变量定义在tcl/lib/ns-default.tcl中,例如

 

需要特别注意的是,没修改一次默认值,都需要重新编译NS2,方能使之生效.

在ns2源码目录下的Makefile文件中,

NS_TCL_LIB = \
tcl/lib/ns-compat.tcl \
tcl/lib/ns-default.tcl \
tcl/lib/ns-errmodel.tcl \
tcl/lib/ns-lib.tcl \
......
$(NS_TCL_LIB_STL)


$(GEN_DIR)ns_tcl.cc: $(NS_TCL_LIB)
$(TCLSH) bin/tcl-expand.tcl tcl/lib/ns-lib.tcl $(NS_TCL_LIB_STL) | $(TCL2C) et_ns_lib > $@

(原以为,Tcl脚本会被ns程序解释的,原来是被链接进去了)


如果没有指定默认值,在Tcl中new的时候,会打印警告信息,但是不会影响程序执行(还没有发现什么问题)

 

PS:set{}也是定义在tclcl/embedded-tclobj.cc中,

 

注意看$self instvar -parse-part1 $var,再次调用了instvar。奇怪的是,OTclOInstVarMethod()没有对-parse-part1选项的处理操作,而在dispatch_instvar()则有相应的处理。(已经用eclipse搜索了pare-part1,只在dispatch_instvar()找到)

你可能感兴趣的:(NS2,NS2)