command()函数与ns的分裂对象模型TclCL有关。这里的代码来自aomdv.cc,相关解释学习自NS与网络模拟一书。
与分裂对象模型有关的类主要为TclObject和TclClass。TclObject类是所有编译类(c++)的基类,SplitObject类是所有解释类(TclCL)类的基类,而类TciClass包含了这两个类间的映射关系。
创建对象时,用户通过tcl脚本创建解释对象的同时在c++类结构中创建了对应的类。通过变量绑定bind可以同时在OTcl类和c++类中访问和修改成员变量。
static class AOMDVclass : public TclClass { public: AOMDVclass() : TclClass("Agent/AOMDV") {} TclObject* create(int argc, const char*const* argv) { assert(argc == 5); //return (new AODV((nsaddr_t) atoi(argv[4]))); return (new AOMDV((nsaddr_t) Address::instance().str2addr(argv[4]))); } } class_rtProtoAOMDV;以上的AOMDVclass继承自TclClass,其中包含了构造函数和create()函数。构造函数直接使用父类TclClass的构造函数,而create()函数使用new创建了相应的c++类(顺便做了对应类的登记工作)。
create()函数的参数组包括:
(1)argv[0],对象名
(2)argv[1-3],$self,$class,$proc.
(3)argv[4],提供给用户的任意附加参数。
AOMDV::AOMDV(nsaddr_t id) : Agent(PT_AOMDV), btimer(this), htimer(this), ntimer(this), rtimer(this), lrtimer(this), rqueue() { // AOMDV code aomdv_max_paths_ = 3; bind("aomdv_max_paths_", &aomdv_max_paths_); aomdv_prim_alt_path_len_diff_ = 1; bind("aomdv_prim_alt_path_len_diff_", &aomdv_prim_alt_path_len_diff_); index = id; seqno = 2; bid = 1; LIST_INIT(&nbhead); LIST_INIT(&bihead); logtarget = 0; AOMDVifqueue = 0; }以上是AOMDV类的构造函数,其中包含了变量的绑定bind。通过bind可以建立双向的绑定,使得OTcl类或对应的c++类中任意一边的变量值改变时,另一边也跟着改变。
int AOMDV::command(int argc, const char*const* argv) { if(argc == 2) { Tcl& tcl = Tcl::instance(); if(strncasecmp(argv[1], "id", 2) == 0) { tcl.resultf("%d", index); return TCL_OK; } // AOMDV code - should it be removed? if (strncasecmp(argv[1], "dump-table", 10) == 0) { printf("Node %d: Route table:\n", index); rtable.rt_dumptable(); return TCL_OK; } if(strncasecmp(argv[1], "start", 2) == 0) { btimer.handle((Event*) 0); #ifndef AOMDV_LINK_LAYER_DETECTION htimer.handle((Event*) 0); ntimer.handle((Event*) 0); #endif // LINK LAYER DETECTION rtimer.handle((Event*) 0); return TCL_OK; }ns command } else if(argc == 3) { if(strcmp(argv[1], "index") == 0) { index = atoi(argv[2]); return TCL_OK; } else if(strcmp(argv[1], "log-target") == 0 || strcmp(argv[1], "tracetarget") == 0) { logtarget = (Trace*) TclObject::lookup(argv[2]); if(logtarget == 0) return TCL_ERROR; return TCL_OK; } else if(strcmp(argv[1], "drop-target") == 0) { int stat = rqueue.command(argc,argv); if (stat != TCL_OK) return stat; return Agent::command(argc, argv); } else if(strcmp(argv[1], "if-queue") == 0) { AOMDVifqueue = (PriQueue*) TclObject::lookup(argv[2]); if(AOMDVifqueue == 0) return TCL_ERROR; return TCL_OK; } // AODV ns-2.31 code else if (strcmp(argv[1], "port-dmux") == 0) { dmux_ = (PortClassifier *)TclObject::lookup(argv[2]); if (dmux_ == 0) { fprintf (stderr, "%s: %s lookup of %s failed\n", __FILE__, argv[1], argv[2]); return TCL_ERROR; } return TCL_OK; } } return Agent::command(argc, argv); }要在OTcl类中调用c++类对象的方法,就需要使用command()函数。该函数对传递的参数进行匹配,若匹配成功,则进入相应的处理函数并返回TCL_OK。若没有匹配的操作,则调用父类的command()方法且返回相应的状态值。
下面是涉及到的几个tips。
1.strncasecmp:
表头文件:#include <string.h>
函数定义:int strncasecmp(const char *s1, const char *s2, size_t n)
函数说明:strncasecmp()用来比较参数s1和s2字符串前n个字符,比较时会自动忽略大小写的差异。
返回值 :若参数s1和s2字符串相同,则返回0; 若s1大于s2,则返回大于0的值; 若s1小于s2,则返回小于0的值。
2.(int agrc,const char* const* argv):
在NS2代码库中,经常看到以(int agrc,const char* const* argv)为参数的函数。agrc表示参数的个数。argv是指向函数参数列表的指针,其中argv[0]是函数名,argv[1]~argv[argc-1]是函数的参数。
argv其实就是个二级指针(即是指向指针的指针),第一个const修鉓是表示argv指向的指针指向了一个常量,不能修改;第二个const修鉓是表示argv指向的指针是个常量,不能对其进行增减操作。argv[0]其实就是argv指向的第一个指针(char* 类型指针),它实际上指向 一个以'\'结束的字符串。按照助记法,const char* const* argv应读为 argv is (a pointer to (a const pointer ( to a const char))).
关于指针的助记法:
Bjarne在他的The C++ Programming Language里面给出过一个助记的方法:
把一个声明从右向左读。
char * const cp; ( * 读成 pointer to )
cp is a const pointer to char
const char * p;
p is a pointer to const char;