NS2中OTcl 和C++ 的连接 (一)

本文翻译自《Introduction to Network Simulator NS2》中第三章,Linkage BetweenOTcl and C++ in NS2,只翻译了其中重要的段落,略过了其他太细节的解释,但不影响阅读和理解。

NS2是一个面向对象的网络模拟器,由OTcl语言和C++语言编就。其中前者负责前台工作(例如用户接口),后者负责后台运算即执行仿真运算。如图1所示,两中语言

image                                                                                        图1

的类架构可以是独立的,也可以由TclCL(称为OTcl/C++接口)类连接起来。左右连个框图中都有两种类型的C++或OTcl类。第一种包括在OTcl和C++之间有联系的类(如左框图的右下角和右框图的左下角)。文献中一般把这些OTcl类和C++类分别称为解释体系(Interpretedhierarchy)和编译体系(Compiledhierarchy)。第二种包括没有相互联系的OTcl类和C++类。这第二种类既不属于解释体系,也不属于编译体系。在这章中我们主要讨论OTcl语言和C++语言如何组成NS2.

TclCl由C++语言写成,包括以下六种主要的类。

1 Tcl 类:提供从编译体系访问解释体系的方法(Method).

2 InstVar 类:负责把两种体系中的成员函数对应的绑定。

3 TclObject 类:是编译体系中所有C++模拟对象的基类。

4 TclClass 类:将解释体系中的类名一一对应于编译体系中的对应类名。

5 TclCommand 类:提供从解释体系访问编译体系的全局方法。

6 EmbeddedTcl 类:把OTcl 脚本转化为C++代码。

3.1 NS2中为什么由两种语言编写?

为什么是两种语言呢?简单的说,NS2使用OTcl语言建立和配置网络,利用C++语言执行仿真。所有的C++代码需要编译并被链接成可执行文件。由于NS2的代码量相当庞大,编译时间必然不短。另一方面,OTcl属于解释器而非编译器,OTcl文件中的任何改变都不需要编译。然而,由于OTcl并没有把所有的代码都转化为机器语言,每行代码都需要更多的执行时间。总之,C++运行速度快但编译修改时间长,适合运行大规模仿真。OTcl运行速度慢但修改时间短,适合运行小规模但参数任意可变的仿真。有鉴如此,NS2吸收了两种语言的优点。

NS2手册提供了如下准则来选择代码语言:

Using OTcl

     ---配置,调整,或一次性仿真。抑或执行现有的NS2模块。

        这中选择十分适合大多数的初学者。因为它不需要对NS2复杂地内部机制的深入理解。但是,现有的NS2模块十分有限,这种选择可能无法满足很多读者。

Using C++

    ---当读者要处理数据包或者修改现有的NS2模块。

       这种选择可能使大多数的NS2初学者感到沮丧。本书的主要的目标是帮助读者理解NS2的内部结构,使读者修改NS2模块时得心应手。

理论上说,我们有三种方式来开发C++程序。第一种称为“BasicC++”,是三种方法中最简单的一种,只包括基本的C++结构。这种方式存在灵活性问题,因为系统中任何参数的改变都需要重新编译。着眼于灵活性问题,第二种方法称为“C++coding with inputarguments”带有输入参数的C++程序。当系统参数发生变化,我们只需简单的改变输入参数而不需要重新编译。但是,第二种方式的重要问题是:当输入参数很多时,调用命令会相当长且复杂。第三种方式称为“C++coding with configuration files“-把所有的系统参数写入配置文件。这种方式解决了灵活性问题和复杂的调用问题。需要改变系统参数时,只需简单的改变配置文件。事实上,NS2正式基于这种方式开发的。

3.2 Tcl类

Tcl 类是作为OTcl域用户接口的一个C++类,它提供以下操作的方法:

1 获取Tcl句柄。(instance 函数)

2 在c++域內调用OTcl程序。(eval(),evalc(),evalf()等函数)

3 从/向解释器接收或传递结果。(result(),resultf()函数)

4 用统一的格式报告错误和终止程序 。(error()函数)

5 检索TclObjects对象的引用(enter(),delete(),lookup()函数)

3.2.1 获得Tcl 类实例的引用

在c++中,我们通过类的对象来调用该类的成员函数。为了调用Tcl类的函数,我们需要一个Tcl类的对象。Tcl类提供instance()函数来获取静态的Tcl变量:

Tcl&tcl=Tcl::instance();

这里,instance()函数返回Tcl的静态变量instance_。由于它是静态的,所以在仿真中将会只有一个Tcl对象instance_。因此,任何时候用以上语句检索一个Tcl对象时都会返回同一个Tcl对象。获取了Tcl对象之后,我们就可以调用Tcl类中的成员函数了。

3.2.2 调用Tcl 程序

在编写C++程序的时候,我们可能需要调用某个OTcl实例程序(Instproc)。例如,我们可能要通过解释体系中Simulator的instproc:now()函数来获取当前仿真时间。Tcl类提供以下函数来调用OTcl程序。例如,下面的C++程序告诉OTcl在屏幕上打印“Overall Packet Delay is10.0 seconds“。

i)Tcl::eval_r(char *str):通过解释器执行存储在变量str中的命令语句。例如:

Tcl&=Tcl::instance();

char s[128];

strcpy(s,”puts [Overall Packet Delay is 10.0 seconds]”);

tcl.evalc(s);

ii)Tcl::evalc(const char*str):执行命令语句"str”.例如

Tcl& tcl=Tcl::instance();

tcl.evalc(“puts [Overall Packet Delay is 10.0 seconds]”);

和前个函数不同。前者把指向字符变量的指针作为输入参数(char*),此函数把字符作为输入变量(const char*)。

Tcl::eval_r():函数执行已经被存储进内部变量bp_的命令。例如

Tcl&tcl=Tcl::instance();

char s[128];

sprintf(tcl.buffer(),”puts [Overall Packet Delay is 10.0seconds]”);

tcl.eval_r();

其中,tcl::buffer()把内容交给内部变量bp。

iii)Tcl::evalf(const char*fmt,…);像C++中的printf一样使用fmt。例如

Tcl&tcl=Tcl::instance();

float delay =10.0;

tcl.evalf(“puts [Overall Packet Delay is %2.1fseconds]”,delay);

3.2.3 从/向解释器接收或者传递结果

我们有时也需要向/从解释器传递或获取数值。例如在上面的例子中,我们需要把包时延传递给解释器而不仅打印出来。Tcl类提供了3个函数来回传数值。

i)Tcl::result(const char*fmt):把字符作为结果传递给解释器。例如:

   Tcl&tcl=Tcl::instance();

   tcl.result(“10”);

   return TCL_OK;

ii)Tcl::resultf(const char* result,…);用类似于C++中的printf(…)格式来把数值传递给解释器。

    例子3.2 :使用chain类中的retrundelay命令返回C++变量dealy。具体执行如下:

             Tcl&tcl=Tcl::instance();

             tcl.resultf(“%1.1f”,delay);

             return TCL_OK;

    在OTcl域,下面的代码将C++的chain类中的变量delay存储在d中。

             set chain [new Chain]

             set d [$Chain returnDelay]

iii)Tcl::result(void);从解释器中检索结果(当C++程序调用一个OTcl命令时,解释器将执行结果保存在私有成员变量tcl_->result中,用户必须用tcl.result(void)来uoqu执行结果,需要注意的是,结果是字符串,必须被转化为适当的类型)。例如,下面的语句把OTcl变量d存储在C++变量delay中。

      Tcl&tcl=Tcl::instance();

      tcl.evalc(“$d”);

      char* delay=tcl.result();

3.2.4  报告错误和终止程序

Tcl提供error()函数用统一的格式来终止程序。这个函数仅仅把储存在“str”和tcl->result(…)中的错误信息打印到屏幕上。

Tcl::error(const char* str)

Tcl::error(str)和ruturnTCL_ERROR的区别如下:前者仅仅打印错误信息和退出程序。后者出现时,NS2会追踪错误(这些错误可能不止一处)。最后,用户可以使用追踪到的这些错误来恢复程序、定位错误点或者打印出错误信息。

3.2.5 检索TclObjects的引用

我们记得,解释体系的对象总是有一个对应的编译对象(称为影子对象)。在某些情况下,我们可能需要获得某个解释对象的影子对象。NS2把两种类型对象的映射关系储存在一个哈希表中。Tcl类提供下面几个函数来输入,删除,或者检索哈希表中的实体。

1 cl::enter(TclObject*o);把对象*o插入哈希表中,并把*o对应的OTcl名字保存在保护变量name_中。此函数在TclClass:creat_shadow(…)被调用。

2 Tcl::delete(TclObject*o);把哈希表中对应*o的实体删除。此函数在TclClass:delete_shadow(…)被调用。

3 Tcl::lookup(char*str);返回名字为“str”的实体。

image

现在我们来讨论程序3.4中的代码。这里argv[2]是由OTcl传入的输入参数。第8行使用TclObject::lookup(argv[2])函数来获取对应于argv[2]的影子函数。得到的对象被转化为NsObject类,存储于变量*target_中。注意,command命令的细节将在3.44中讲述。

 

待续。


  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值