NS2 Tclcl机制分析, 编译层/解释层交互过程 ------ NS2学习日记 (2)
2014-12-16 16:16
736 查看
NS2中使用了tclcl模块实现编译层与解释层的交互,下面将依据TclClass/TclObject以及SatNode模块简要分析交互的过程 。
如果有疏漏的地方希望能够帮忙指正。
详细的用户手册可以参见文档《NS Manual》/《ns-chinese-manual》。
Tclcl 模块: 主要包含了Tcl, TclObject, TclClass, InstVar, TclCommand, EmbeddedTcl等c++类:
Tcl 类: 是解释器的实例, 并提供了c++代码访问解释器的接口。
TclObject 类 : 所有编译层类的基类, 我们所编写的新模块必须继承该类或它的子类。
TclClass 类 : 向解释器注册解释层类, 并将对应的解释层类与编译层类进行绑定(TclClass::create方法)。
InstVar 类 : 编译层类与解释层类中成员变量进行绑定。
TclCommand 类 : 注册解释器全局命令。
EmbededTcl 类 : 动态实现装载脚本。
下面主要进行TclObject, TclClass 的分析, 模块SatNode为例, 进行如下分析 :
1. 如何向NS添加一个模块, 在添加模块的过程中是怎样的执行过程 ?
2. 在ns中使用新的类时是怎样的执行过程, 如何实现 ?
3. 创建解释对象时, 如何实现变量的绑定 ?
1. 如何向NS添加一个模块, 在添加模块的过程中是怎样的执行过程 ?
首先需要创建两个类, 与该模块对应的TclObject类以及TclClass类。
以SatNode为例, 我们需要创建
SatNodeClass继承了TclClass类, 该静态类创建了一个全局实例对象, 所以在ns运行过程中会实例化对象class_satnode, 从而执行SatNodeClass的初始化函数。
SatNodeClass的初始化函数调用了父类TclClass的初始化函数, TclClass("Node/SatNode"), TclClass的初始化函数如下:
bind函数中tcl.evalf("SplitObject register %s", classname_)语句为调用解释器api(evalf)执行tcl语句, 功能是向解释器内注册新类, 类名为classname也就是"Node/SatNode", 在注册过程中将进行一定的层次分析, 可以参见SplitObject proc register className{}(在tclcl/tcl-object.tcl内)。
接着向解释器内该类添加方法, create-shadow/deleteshadow函数, 这将在解释类的实例创建时调用。
同时SatNodeClass覆盖了TclClass的虚函数, 并且创建一个新的编译实例(new SatNode)作为返回参数, 这也会在解释层实例创建时调用。
至此, 已经向解释器注册了新的模块SatNode。(模块的具体实现在下面介绍, 新模块的编译链接可以参考网上的文档)
2. 在ns中使用新的类时是怎样的执行过程, 如何实现 ?
在解释器中, 我们可以使用 new Node/SatNode创建一个解释类的实例, 参见Simulator instproc newsatnode {} (ns/tcl/lib/ns-lib.tcl)。
这将调用函数 proc new { className args } (/tclcl/tcl-object.tcl)
在create函数中, 将根据解释类的层次, 对当前的解释类进行继承层次解析,并依次分配内存。,之后eval [list $obj] init $args调用当前解释类的init函数(注 : 一般所有的init函数第一条语句都是调用父类的init函数,而SplitObject是所有TclClass创建解释类的基类, 所以会首先调用SplitObject的init函数)。
接下来, 将根据解释器内创建类实例时提供的参数进行TclObject的初始化, o->init(argc - 2, argv + 2), o是create函数创建的解释类实例, 调用init函数进行变量的绑定。注意该函数是一个虚函数, 可以在实现的类中进行自己的定义。
而接下来OTclAddPMethod(OTclGetObject(interp, argv[0]), "cmd", (Tcl_CmdProc *) dispatch_cmd, (ClientData)o, 0)等语句则向解释器内该解释类实例添加了两个命令cmd和instvar。(关于cmd的内容可以前面介绍的参考文档中)
至此, 一个解释类实例的创建过程已经完成。
3. 创建解释对象时, 如何实现变量的绑定 ?
在上一节中讲到在调用编译类的构造函数时将会依次调用父类的构造函数, 同时在ns中, 一般在编译类的构造函数中进行instvar的绑定, 如在SatNode中 :
在TclObject的实例中, 所有的InstVar是存放在一个链表中的, 在init函数中进行了入表和初始化的操作。
当所有的构造函数完成, 也就实现了解释类成员变量和编译类成员变量的绑定。
如果有疏漏的地方希望能够帮忙指正。
详细的用户手册可以参见文档《NS Manual》/《ns-chinese-manual》。
Tclcl 模块: 主要包含了Tcl, TclObject, TclClass, InstVar, TclCommand, EmbeddedTcl等c++类:
Tcl 类: 是解释器的实例, 并提供了c++代码访问解释器的接口。
TclObject 类 : 所有编译层类的基类, 我们所编写的新模块必须继承该类或它的子类。
TclClass 类 : 向解释器注册解释层类, 并将对应的解释层类与编译层类进行绑定(TclClass::create方法)。
InstVar 类 : 编译层类与解释层类中成员变量进行绑定。
TclCommand 类 : 注册解释器全局命令。
EmbededTcl 类 : 动态实现装载脚本。
下面主要进行TclObject, TclClass 的分析, 模块SatNode为例, 进行如下分析 :
1. 如何向NS添加一个模块, 在添加模块的过程中是怎样的执行过程 ?
2. 在ns中使用新的类时是怎样的执行过程, 如何实现 ?
3. 创建解释对象时, 如何实现变量的绑定 ?
1. 如何向NS添加一个模块, 在添加模块的过程中是怎样的执行过程 ?
首先需要创建两个类, 与该模块对应的TclObject类以及TclClass类。
以SatNode为例, 我们需要创建
class SatNode : public Node {} static class SatNodeClass : public TclClass { public: SatNodeClass() : TclClass("Node/SatNode") {} TclObject* create(int , const char*const* ) { return (new SatNode); } } class_satnode;其中Node类为TclObject的子类。
SatNodeClass继承了TclClass类, 该静态类创建了一个全局实例对象, 所以在ns运行过程中会实例化对象class_satnode, 从而执行SatNodeClass的初始化函数。
SatNodeClass的初始化函数调用了父类TclClass的初始化函数, TclClass("Node/SatNode"), TclClass的初始化函数如下:
TclClass::TclClass(const char* classname) : class_(0), classname_(classname) { if (Tcl::instance().interp()!=NULL) { bind(); } ... }初始化函数将执行TclClass::bind函数:
void TclClass::bind() { Tcl& tcl = Tcl::instance(); tcl.evalf("SplitObject register %s", classname_); class_ = OTclGetClass(tcl.interp(), (char*)classname_); OTclAddIMethod(class_, "create-shadow", (Tcl_CmdProc *) create_shadow, (ClientData)this, 0); OTclAddIMethod(class_, "delete-shadow", (Tcl_CmdProc *) delete_shadow, (ClientData)this, 0); otcl_mappings(); }
bind函数中tcl.evalf("SplitObject register %s", classname_)语句为调用解释器api(evalf)执行tcl语句, 功能是向解释器内注册新类, 类名为classname也就是"Node/SatNode", 在注册过程中将进行一定的层次分析, 可以参见SplitObject proc register className{}(在tclcl/tcl-object.tcl内)。
接着向解释器内该类添加方法, create-shadow/deleteshadow函数, 这将在解释类的实例创建时调用。
同时SatNodeClass覆盖了TclClass的虚函数, 并且创建一个新的编译实例(new SatNode)作为返回参数, 这也会在解释层实例创建时调用。
至此, 已经向解释器注册了新的模块SatNode。(模块的具体实现在下面介绍, 新模块的编译链接可以参考网上的文档)
2. 在ns中使用新的类时是怎样的执行过程, 如何实现 ?
在解释器中, 我们可以使用 new Node/SatNode创建一个解释类的实例, 参见Simulator instproc newsatnode {} (ns/tcl/lib/ns-lib.tcl)。
这将调用函数 proc new { className args } (/tclcl/tcl-object.tcl)
proc new { className args } { set o [SplitObject getid] if [catch "$className create $o $args" msg] {该函数又会调用 create 函数。
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} }
在create函数中, 将根据解释类的层次, 对当前的解释类进行继承层次解析,并依次分配内存。,之后eval [list $obj] init $args调用当前解释类的init函数(注 : 一般所有的init函数第一条语句都是调用父类的init函数,而SplitObject是所有TclClass创建解释类的基类, 所以会首先调用SplitObject的init函数)。
SplitObject instproc init args { $self next if [catch "$self create-shadow $args"] { error "__FAILED_SHADOW_OBJECT_" "" } }init函数将调用该解释类的create-shadow函数, 该函数在SatNodeClass创建实例的过程中已经向注册器中注册。
int TclClass::create_shadow(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { TclClass* p = (TclClass*)clientData; 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()); OTclAddPMethod(OTclGetObject(interp, argv[0]), "cmd", (Tcl_CmdProc *) dispatch_cmd, (ClientData)o, 0); OTclAddPMethod(OTclGetObject(interp, argv[0]), "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函数, 创建一个对应TclObject的实例对象, create函数一般创建一个对应TclObject的实例作为返回参数。(注意构造函数调用过程中将会依次调用父类的构造函数, 同时一般会在构造函数内进行解释实例以及编译实例的变量绑定工作, 在下一节分析)
接下来, 将根据解释器内创建类实例时提供的参数进行TclObject的初始化, o->init(argc - 2, argv + 2), o是create函数创建的解释类实例, 调用init函数进行变量的绑定。注意该函数是一个虚函数, 可以在实现的类中进行自己的定义。
而接下来OTclAddPMethod(OTclGetObject(interp, argv[0]), "cmd", (Tcl_CmdProc *) dispatch_cmd, (ClientData)o, 0)等语句则向解释器内该解释类实例添加了两个命令cmd和instvar。(关于cmd的内容可以前面介绍的参考文档中)
至此, 一个解释类实例的创建过程已经完成。
3. 创建解释对象时, 如何实现变量的绑定 ?
在上一节中讲到在调用编译类的构造函数时将会依次调用父类的构造函数, 同时在ns中, 一般在编译类的构造函数中进行instvar的绑定, 如在SatNode中 :
SatNode::SatNode() : ragent_(0), trace_(0), hm_(0) { bind_bool("dist_routing_", &dist_routing_); }调用bind_bool函数,进行成员变量“dist_routing_”的绑定, bind_bool实现如下。
#define TOB(FUNCTION, C_TYPE, INSTVAR_TYPE, OTHER_STUFF) \ void TclObject::FUNCTION(const char* var, C_TYPE* val) \ { \ create_instvar(var); \ OTHER_STUFF; \ init(new INSTVAR_TYPE(var, val), var); \ } TOB(bind_bool, int, InstVarBool, ;)通过“#define” 功能实现了bind_bool的定义, 在函数中存在两个语句create_instvar(var)以及init(new InstVarBool(var, val), var), 其中create_instvar定义如下:
void TclObject::create_instvar(const char* var) { /* * XXX can't use tcl.evalf() because it uses Tcl_GlobalEval * and we need to run in the context of the method. */ char wrk[256]; sprintf(wrk, "$self instvar %s", var); Tcl_Eval(Tcl::instance().interp(), wrk); }调用instvar命令, 进行解释器内全局变量的声明(?), 接着init(new InstVarBool(var, val), var)语句进行变量的绑定以及初始化的工作。
void TclObject::init(InstVar* v, const char* var) { insert(v); v->init(var); }
在TclObject的实例中, 所有的InstVar是存放在一个链表中的, 在init函数中进行了入表和初始化的操作。
当所有的构造函数完成, 也就实现了解释类成员变量和编译类成员变量的绑定。
相关文章推荐
- Linux 学习笔记_13_2_LAMP环境编译(下) --编译过程及分析
- android学习总结:Android编译过程及脚本的分析
- ASP.NET运行机制原理 ---浏览器与IIS的交互过程 自己学习 网上查了下别人写的总结的很好 就转过来了 和自己写的还好里嘻嘻
- Java学习之类加载全过程_JVM内存分析_反射机制核心原理_常量池理解
- Linux下对NS2项目的仿真分析全过程代码(从tcl脚本-awk文本处理-gnuplot)
- GCC Coverage代码分析-编译过程自动化及对链接的解释
- c语言学习笔记(10)编译器编译过程分析
- 05-S3C2440学习之内核(初步)编译、配置过程分析
- java学习心得(一):代码的简单编译和解释的前期过程
- NS2 分裂机制及代码分析<一>---解释类成员变量与编译类成员变量互操作
- GCC Coverage代码分析-编译过程自动化及对链接的解释
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux内核抢占实现机制分析
- 嵌入式Linux文件系统及其存储机制分析 - farsight嵌入式学习专栏 - CSDNBlog
- TCL解释器与C++代码交互过程?
- 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】Linux系统调用的实现机制分析
- Web Analytics工具domodomain的B/S交互实现机制分析
- 学习ns2过程中,如何在vim中使用cscope高效阅读代码
- DirectShow学习之三媒体播放过程分析
- 用Bochs学习Minix(2)-启动过程分析
- Java NIO 学习笔记 selector 行为机制分析(select操作 cancel操作)