翻譯自NS by Example,見笑
圖18
1.將C++類導(dǎo)出至OTcl
假定你已經(jīng)使用C++創(chuàng)建了一個(gè)新的網(wǎng)絡(luò)對(duì)象類,比如繼承自Agent類的MyAgent,并且你想在OTcl中創(chuàng)建該對(duì)象的實(shí)例。為此,你必須定義一個(gè)繼承自TclClass的連接對(duì)象,比如MyAgentClass。該連接對(duì)象用于創(chuàng)建一個(gè)有著特定名稱(如該例的Agent/MyAgentOtcl)的OTcl對(duì)象,并且在OTcl對(duì)象和C++對(duì)象(如該例中的MyAgent)之間創(chuàng)建連接。并且在C++對(duì)象的create成員函數(shù)中指定了實(shí)例的開始過(guò)程(launching procedure)。
2.將C++變量導(dǎo)出至OTcl
假設(shè)你的新的C++對(duì)象MyAgent中有兩個(gè)參數(shù)變量,比如my_var1和my_var2,這兩個(gè)變量必須夠夠通過(guò)使用OTcl模擬腳本來(lái)很容易地進(jìn)行配置(或改變)。為此,你應(yīng)當(dāng)對(duì)C++類中的每個(gè)你想導(dǎo)出的變量使用綁定函數(shù)。綁定函數(shù)在對(duì)應(yīng)的OTcl對(duì)象類(如Agent/MyAgentOtcl)中使用給定的名字(第一個(gè)參數(shù))創(chuàng)建新的成員變量,并且建立在OTcl變量和C++變量(變量的地址通過(guò)第二個(gè)參數(shù)確定)之間的雙向綁定。圖19顯示了怎樣對(duì)圖18中的兩個(gè)變量my_var1和my_var2進(jìn)行綁定。
圖19
注意,為了在對(duì)象的實(shí)例被創(chuàng)建時(shí)建立綁定,綁定函數(shù)應(yīng)放置在MyAgent的構(gòu)造函數(shù)內(nèi)。NS支持4種不同的綁定函數(shù),適用于如下5種不同的變量類型:
- bind(): 浮點(diǎn)型和整型變量
- bind_time(): 時(shí)間變量
- bind_bw(): 帶寬變量
- bind_bool(): 布爾變量
通過(guò)這種方法,使用OTcl腳本來(lái)設(shè)計(jì)并進(jìn)行模擬的用戶可以對(duì)使用C++實(shí)現(xiàn)的網(wǎng)絡(luò)組件中的可以配置的參數(shù)(或者變量值)進(jìn)行訪問(wèn)或者改變。注意,強(qiáng)烈建議無(wú)論何時(shí)導(dǎo)出一個(gè)C++變量,都應(yīng)當(dāng)同時(shí)在ns-2/tcl/lib/ns-lib.tcl中設(shè)置該變量的默認(rèn)值。否則,當(dāng)創(chuàng)建新對(duì)象的實(shí)例時(shí)會(huì)有警告信息。
3 .將C++對(duì)象控制命令導(dǎo)出至OTcl
圖20. 一個(gè)OTcl命令解釋器除了要導(dǎo)出你的C++對(duì)象的一些變量之外,你可能還想讓OTcl得到你的C++對(duì)象的控制權(quán)。這可以通過(guò)定義你的C++對(duì)象(例如,MyAgent)的command成員函數(shù)來(lái)實(shí)現(xiàn)。該成員函數(shù)充當(dāng)OTcl命令的解釋器的角色。事實(shí)上,在C++對(duì)象的command成員函數(shù)中定義的OTcl命令看起來(lái)與相應(yīng)的展示給用戶的OTcl對(duì)象的成員函數(shù)相同。圖20是一個(gè)一個(gè)對(duì)圖18中的MyAgent對(duì)象的command成員函數(shù)的定義例子。
圖20
當(dāng)匹配MyAgent對(duì)象的OTcl影像對(duì)象的實(shí)例在OTcl內(nèi)被創(chuàng)建(例如,使用了set myagent [new Agent/MyAgentOtcl]命令),并且用戶試圖調(diào)用該對(duì)象的成員函數(shù)時(shí)(例如,使用$myagent call-my-priv-func),OTcl會(huì)在OTcl對(duì)象中搜索給定的成員函數(shù)名。如果給定的成員函數(shù)名未能找到,那么將會(huì)調(diào)用MyAgent::command來(lái)以“參數(shù)名/參數(shù)值”的形式傳遞被調(diào)用的OTcl成員函數(shù)的參數(shù)的名稱和參數(shù)值。如果在command成員函數(shù)中存以在被調(diào)用的OTcl成員函數(shù)的名稱定義的操作,那它執(zhí)行請(qǐng)求并返回結(jié)果。如果不存在,command成員函數(shù)的祖先會(huì)被依次遞歸調(diào)用,直到發(fā)現(xiàn)該名稱。如果在任何一個(gè)祖先中都未能發(fā)現(xiàn)該名稱,則返回給OTcl對(duì)象一個(gè)錯(cuò)誤信息,并且該OTcl對(duì)象顯示給用戶一條錯(cuò)誤信息。通過(guò)這種方式,用戶可以在OTcl內(nèi)控制C++對(duì)象的行為。
4.從C++中執(zhí)行OTcl命令
如果你用C++實(shí)現(xiàn)了一個(gè)新的網(wǎng)絡(luò)對(duì)象,你或許從C++對(duì)象中執(zhí)行一條OTcl命令。圖21顯示了圖18中的MyAgent對(duì)象的MyPrivFunc成員函數(shù)的實(shí)現(xiàn)。它讓OTcl解釋器打印出私有成員變量my_var1和my_var2的值。
為了從C++中執(zhí)行一條OTcl命令,你應(yīng)當(dāng)取得到Tcl::instance()的引用(reference),該引用被聲明為一個(gè)成員變量。它給你提供了若干函數(shù),使用這些函數(shù),你可以給解釋器傳遞OTcl命令(MyPrivFunc的第一行)。該例展示了想解釋器傳遞OTcl命令的兩種方法。要取得OTcl命令傳遞函數(shù)的完整列表,請(qǐng)參考NS手冊(cè)。
圖21
5.編譯,運(yùn)行和測(cè)試
至今為止,我們使用MyAgent例子檢測(cè)了NS中基本的OTcl連接的可用性。假設(shè)運(yùn)行并且測(cè)試該例子可以幫助讀者理解得更加深刻。我們給了一個(gè)可以幫助你編譯、運(yùn)行、測(cè)試MyAgent的步驟。
1. 下載ex-linkage.cc文件,并將其保存在NS2目錄下
2. 打開Makefile,將ex-linkage.o添加至目標(biāo)文件列表中
3. 使用make命令重新編譯NS
4. 下載ex-linkage.tcl,該文件包含了測(cè)試MyAgent的OTcl命令。(圖22顯示了輸入的腳本和相應(yīng)的結(jié)果)
5. 使用ns ex-linkage.tcl命令運(yùn)行OTcl腳本
該類寫好后,將.h文件(如果有的話,一般都會(huì)有)和.cc文件放置于 ns-allinone-2.28/ns-2.28/目錄下自己新建的一個(gè)子目錄(假如叫bear)中,然后打開 ns-allinone-2.28/ns-2.28/Makefile文件,將 bear/ex-linkage.o 添加到該Makefile的OBJ_CC宏定義中,以便在對(duì)ns進(jìn)行編譯的時(shí)候能夠找到該模塊的源文件并將其編譯到ns中;此外,打開 ns-allinone-2.28/ns-2.28/tcl/lib/ns-default.tcl文件,為該類對(duì)應(yīng)的Otcl類設(shè)置一些初始值。最后,利用Makefile,對(duì)整個(gè)ns重新編譯,就可以是使用新添加的模塊了。
以下給出NS by Example教程中的一個(gè)完整例子:
//myagent.cc
#include
#include
#include "agent.h"
class MyAgent : public Agent {
public:
MyAgent();
protected:
int command(int argc, const char*const* argv);
private:
int my_var1;
double my_var2;
void MyPrivFunc(void);
};
static class MyAgentClass : public TclClass {
public:
MyAgentClass() : TclClass("Agent/MyAgentOtcl") {}
TclObject* create(int, const char*const*) {
return(new MyAgent());
}
} class_my_agent;
MyAgent::MyAgent() : Agent(PT_UDP) {
bind("my_var1_otcl", &my_var1);
bind("my_var2_otcl", &my_var2);
}
int MyAgent::command(int argc, const char*const* argv) {
if(argc == 2) {
if(strcmp(argv[1], "call-my-priv-func") == 0) {
MyPrivFunc();
return(TCL_OK);
}
}
return(Agent::command(argc, argv));
}
void MyAgent::MyPrivFunc(void) {
Tcl& tcl = Tcl::instance();
tcl.eval_r("puts \"Message From MyPrivFunc\"");
tcl.evalf("puts \" my_var1 = %d\"", my_var1);
tcl.evalf("puts \" my_var2 = %f\"", my_var2);
}