现在的位置: 首页 > 综合 > 正文

重载TCLCL中的类

2013年09月04日 ⁄ 综合 ⁄ 共 3217字 ⁄ 字号 评论关闭
1. 问题的由来
在使用BlueHoc的过程中,发现它直接修改了ns2的一些核心文件,那么是否有一种方式可以不必修改NS2核心文件而达到同样的目的呢?
2. 思路
Ns2使用TCL/CL来将OTCL代码与C++代码结合起来,所有的仿真过程都是通过OTCL代码调用的。那么是否可以重载OTCL中相同名称的类,在它们创建时将其指向自己的C++类呢?
3. 测试代码
为了测试使用同名的OTCL类是否可以重载的问题,写了以下的测试代码:
#include <math.h>
#include "tclcl.h"
extern "C" {
#include <otcl.h>
}
 
// 定义一个准备让TCL脚本调用的类,此类必须从TclObject继承
class CTestObject : public TclObject
{
private:
     int m_nCount;
 
public:
     CTestObject()
     {
         m_nCount = 0;
 
         // bind之后就可以在TCL脚本中访问这个类的成员变量了,
         // 对于没有使用bind的变量是无法通过脚本访问的。
         // 在此只展示一个简单的整数类型,如果是复杂数据则需要从TracedVar继承一个子类进行控制
         this->bind("Count", &m_nCount); 
     }
 
     virtual int init(int argc, const char*const* argv)
     {
         // 在脚本中进行实例化时将调用此函数
         printf("CTestObject::init called/n");
         m_nCount = 100;
         return (TCL_OK);
     }
     virtual int command(int argc, const char*const* argv)
     {
         // 当脚本中进行对象的函数调用时将使用此函数
         printf("CTestObject::command called/n");
         if(argc >= 2 && strcmp(argv[1], "GetCount") == 0)
         {
              char result[20];
              sprintf(result, "%d", m_nCount);
              Tcl::instance().result(result);
              return TCL_OK;
         }
         else
              return TclObject::command(argc, argv);
     }
 
};
 
class CSubTestObject : public CTestObject
{
private:
     int m_nSubCount;
 
public:
     CSubTestObject()
     {
         m_nSubCount = 0;
         this->bind("SubCount", &m_nSubCount);
     }
     virtual int init(int argc, const char*const* argv)
     {
         // 在脚本中进行实例化时将调用此函数
         CTestObject::init(argc, argv);
         printf("CSubTestObject::init called/n");
         m_nSubCount = 200;
         return (TCL_OK);
     }
     virtual int command(int argc, const char*const* argv)
     {
         // 当脚本中进行对象的函数调用时将使用此函数
         printf("CSubTestObject::command called/n");
         if(argc >= 2 && strcmp(argv[1], "GetSubCount") == 0)
         {
              char result[20];
              sprintf(result, "%d", m_nSubCount);
              Tcl::instance().result(result);
              return TCL_OK;
         }
         else
              return CTestObject::command(argc, argv);
     }
};
 
// 定义一个TCL脚本中可以使用的类信息
class CTestClass : public TclClass
{
public:
     CTestClass()
         : TclClass("CTestClass")
     {
     }
 
     virtual TclObject* create(int argc, const char* const* argv)
     {
         // 创建对象时将调用此函数,如果CTestObject的构造函数需要参数也可以从这里取得
         printf("CTestClass::create called/n");
         return new CTestObject;
     }
};
 
// 不可使用这种变量的方式,否则无法重载
//static CTestClass _cls;
 
 
// 定义一个TCL脚本中可以使用的类信息
class CSubTestClass : public TclClass
{
public:
     CSubTestClass()
         : TclClass("CTestClass")         // 使用与CTestClass相同的类名称,看看发生的结果
     {
     }
 
     virtual TclObject* create(int argc, const char* const* argv)
     {
         // 创建对象时将调用此函数,如果CTestObject的构造函数需要参数也可以从这里取得
          printf("CSubTestClass::create called/n");
         return new CSubTestObject;
     }
};
 
// 不可使用这种变量的方式,否则无法重载
//static CSubTestClass _sub_cls;
 
// 应先初始化Tcl::init之后再注册类型信息,否则无法重载
void ClassInit()
{
     new CTestClass;
     new CSubTestClass;
}
4. 测试中出现的问题
在此测试过程中,刚开始使用的是静态全局变量进行类的注册,但是发现此种情况下无法重载。跟踪代码时发现,在调用TclClass类构造函数时,如果Tcl::instance()未初始化,则构造函数仅仅是将我们的类放在一个单链表中,而不会立即进行OTCL类和C++类的绑定。注意此链表中后插入的数据是放在表头的,而初始化完成后进行C++类和OTCL类绑定的时候则是从表头向表尾按顺序进行的,因此就造成了这个问题。
根据分析,解决问题的方式也有两种。
第一种仍然使用静态全局变量的方式,但是要更改父类和子类初始化的先后顺序,先定义子类的静态全局变量,再定义父类的静态全局变量。
第二种不使用全局静态变量,而是在Tcl::init()调用完成后再进行类的初始化,如上述代码中的ClassInit(),此时应先初始化父类,再初始化子类。
经过上述调整,即可得到正确的结果。
 
 

 

抱歉!评论已关闭.