一、什么是TypeId
TypeId 实际上它就是一个资源管理器,我们把每一个类都赋予其对应的 TypeId,那么我们可以通过类名找到对应的 TypeId,使得我们代码可以是一个弱关联关系,不一定要在编译阶段就 include 这个类,可以在运行时再通过连接的方式找到对应类!
所以我们用 gcc 是很难编译一个 ns3 代码的,它就不是传统的那种写法,一定需要 waf 来编译。
如果设计模式学的比较好应该可以理解,这个其实是一个策略模式。
我们通过弱解耦的方式把不同的方法引进来,例如我们要把拥塞控制算法从 Reno 换成 BBR,只需要写一个 BBR 的类,然后 Set 进去就好了,而不需要改其他代码(甚至 #include 部分都不用改)。
二、如何注册TypeId
下面是一段官方文档内的代码,关于官方给的TypeId注册与使用示例
class MyObject : public Object
{
public:
static TypeId GetTypeId (void)
{
static TypeId tid = TypeId ("MyObject")
.SetParent (Object::GetTypeId ())
.AddConstructor<MyObject> ()
.AddTraceSource ("MyInteger",
"An integer value to trace.",
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
return tid;
}
MyObject () {}
TracedValue<uint32_t> m_myInt;
};
void
IntTrace (Int oldValue, Int newValue)
{
std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}
int
main (int argc, char *argv[])
{
Ptr<MyObject> myObject = CreateObject<MyObject> ();
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
myObject->m_myInt = 1234;
}
在类MyObject的定义中,可以看到注册Type的流程:
class MyObject : public Object
{
public:
static TypeId GetTypeId (void)
{
static TypeId tid = TypeId ("MyObject")
.SetParent (Object::GetTypeId ())
.AddConstructor<MyObject> ()
.AddTraceSource ("MyInteger","An integer value to trace.",MakeTraceSourceAccessor (&MyObject::m_myInt));
return tid;
}
MyObject () {}
TracedValue<uint32_t> m_myInt;
};
我们可以如下图所示地查找构造函数TypeId()、成员函数SetParent()、AddConstructor()、AddTraceSource()的含义。为了更简单可以在网页中 ctrl+F 查找需要的内容
1、点进绿色字体的链接可以看到,下面这行代码所示的“MyObject”就是接口的名字,而且必须是独一无二,不可以相同的。
static TypeId tid = TypeId ("MyObject")
2、如下图:setParent括号内的是此类的基类。
.SetParent (Object::GetTypeId ())
3、如下图:AddConstructor,记录默认构造函数可访问。
.AddConstructor<MyObject> ()
4、AddTraceSource是为了添加TraceSource,同样可以在TypeId的类中找到这个方法,他的描述是Record a new TraceSource。其参数的含义如下图。
.AddTraceSource ("MyInteger","An integer value to trace.",MakeTraceSourceAccessor (&MyObject::m_myInt));
traceSource我的理解是指一个事件。
所以这段代码的含义可以理解是 记录了一个叫做“MyInteger”的事件,这个事件的描述是"An integer value to trace.",这个事件具体是指m_mInt的值发生变化。
- 更多traceSource的理解可以看这个文档
5、常用的组还有setGropName(),指此类所在的组,例如Cubic的组是Tcp。
AddAttribute用于在此TypeId中记录 新属性。
- [in]name新属性的名称
- [in]help一些描述此属性用途的帮助文本
- [in]flags描述如何读取和/或写入此属性的标志。
- [in]initialValue此属性的初始值。
- [in]accessor关联AttributeAccessor子类的实例。
- [in]checker关联的AttributeChecker子类的实例。
- [in]supportLevel属性的支持/弃用状态。
- [in]supportMsg升级提示(如果不再支持此属性)。如果属性已弃用,则属性行为仍然存在,但应按照提示中的指导更新用户代码。。如果属性是OBSOLETE,则提示应指示属性函数已移动到哪个类,或者不再支持该功能。
例如:
.AddAttribute ("MaxSize",//添加属性:最大尺寸
"The max queue size",//注释,最大队列长度
QueueSizeValue (QueueSize ("1000p")),
MakeQueueSizeAccessor (&QueueDisc::SetMaxSize,
&QueueDisc::GetMaxSize),
MakeQueueSizeChecker ())
三、如何使用TypeId
在如上文中的示例中,我们就注册了一个名字叫“MyObject”的TypeId。类似的我们可以注册其他不同名字的TypeId。
首先查找看是否有这样的类:
[in] name The name of the requested TypeId
[out] tid A pointer to the TypeId instance where the result of this function should be stored.
transport_prot0 = std::string("ns3::TcpCubic") ;
TypeId tcpTid;
bool ok = TypeId::LookupByNameFailSafe (transport_prot0, &tcpTid);
if (!ok) {
std::cout << "TypeId " << transport_prot0 << " not found" << std::endl;
exit(1);
}
通过一个TypeId name找到一个TypeId对象,返回给&tcpTid这个引用。
下一步就是经过上面的查找后,将这个协议栈更改,添加到SocketType属性中。
例如:假如transport_port0是TcpCubic,那就设置左右节点的协议栈为TcpCubic
Ptr<TcpL4Protocol> protol;
Ptr<TcpL4Protocol> protor;
for (uint32_t i = 0; i < num_leaf; ++i) {
protol = leftleaf.Get(i)->GetObject<TcpL4Protocol> ();
protor = rightleaf.Get(i)->GetObject<TcpL4Protocol> ();
if (i < num_cca0) {
protol->SetAttribute ("SocketType", TypeIdValue
(TypeId::LookupByName(transport_prot0)));
protor->SetAttribute ("SocketType", TypeIdValue
(TypeId::LookupByName(transport_prot0)));
}