这里我们讲解一下~/ tclcl / tclcl.h中封装的OTcl解释器的实际实例,并提供了访问该解释器并与之通信的方法。
Tcl类提供了以下方法:
- 获得Tcl对象的引用
- 调用OTcl的函数,通过解释器
- 向解释器取值或者传值
- 报告错误情况并以统一的方式退出
- 存储并查找“TclObjects”。
- 获得对解释器的直接访问
在下面我们将对每一个方法进行详细讲解:
获得Tcl对象的引用
该类的单个实例在~tclcl / Tcl.cc中声明为静态成员变量;
我们必须获取对此实例的引用才能访问本节中描述的其他方法。
访问此实例所需的语句是:
Tcl& tcl = Tcl::instance();
调用OTcl的函数,通过解释器
通过实例tcl调用OTcl命令有四种不同的方法。它们的参数基本不同。
每个函数都将一个字符串传递给解释器,然后在全局上下文中评估字符串的含义。
- 如果解释器返回TCL_OK,这些方法将返回调用者。
- 另一方面,如果解释器返回TCL_ERROR,则方法将报tkerror错误。用户可以重载此过程以选择性地忽略某些类型的错误。
void Tcl::evalc(const char* s)
{
unsigned int n = strlen(s) + 1;
if (n < sizeof(buffer_) - (bp_ - buffer_)) {
char* const p = bp_;
bp_ += n;
strcpy(p, s);
eval(p);
bp_ = p;
} else {
char* p = new char[n + 1];
strcpy(p, s);
eval(p);
delete[] p;
}
}
void Tcl::eval(char* s)
{
int st = Tcl_GlobalEval(tcl_, s);
if (st != TCL_OK) {
int n = strlen(application_) + strlen(s);
if (n > MAX_CODE_TO_DUMP) {
s = "\n[code omitted because of length]\n";
n = strlen(application_) + strlen(s);
};
char* wrk = new char[n + 80];
sprintf(wrk, "tkerror {%s: %s}", application_, s);
if (Tcl_GlobalEval(tcl_, wrk) != TCL_OK) {
fprintf(stderr, "%s: tcl error on eval of: %s\n",
application_, s);
exit(1);
}
delete[] wrk;
//exit(1);
}
}
void Tcl::eval()
{
char* p = bp_;
bp_ = p + strlen(p) + 1;
/*XXX*/
if (bp_ >= &buffer_[1024]) {
fprintf(stderr, "bailing in Tcl::eval\n");
assert(0);
exit(1);
}
eval(p);
bp_ = p;
}
/*
* 按理说,应该是4个,还有一个evalf,但是在Tcl.cc中我没有找到,
* 所以暂时作罢,等日后遇到,
* 再做补充
*/
下面举一些例子来调用这些函数:
Tcl& tcl = Tcl::instance();
char wrk[128];
strcpy(wrk, "Simulator set NumberInterfaces_ 1");
tcl.eval(wrk);
sprintf(tcl.buffer(), "Agent/SRM set requestFunction_ %s", "Fixed");
tcl.eval();
tcl.evalc("puts stdout {hello world}");
tcl.evalf("%s request %d %d", name_, sender, msgid);
向解释器取值或者传值
当解释器调用C ++方法时,它会将结果返回到私有成员变量tcl_->result中。
有两种方法可用于设置此变量。(to)
void result(Tcl_Obj *pObj) {
Tcl_SetObjResult(tcl_, pObj);
}
inline const char* result() const {
return (char *) Tcl_GetStringResult(tcl_);
}
inline char* result() const {
return (tcl_->result);
}
inline void result(const char* p) {
tcl_->result = (char*)p;
}
void resultf(const char* fmt, ...);
/*examples*/
if (strcmp(argv[1], "now") == 0) {
tcl.resultf("%.17g", clock());
return TCL_OK;
}
tcl.result("Invalid operation specified");
return TCL_ERROR;
同样,当C ++方法调用OTcl命令时,解释器会在tcl_->result中返回结果。(from)
tcl.evalc("Simulator set NumberInterfaces_");
char* ni = tcl.result();
if (atoi(ni) != 1)
tcl.evalc("Simulator set NumberInterfaces_ 1");
报告错误情况并以统一的方式退出
此方法提供了一种统一的方法来报告编译代码中的错误。
void Tcl::error(const char* s)
{
if (strlen(s) > MAX_CODE_TO_DUMP) {
s = "\n[code omitted because of length]\n";
};
fprintf(stderr, "%s: \"%s\": %s\n", application_, s, tcl_->result);
exit(1);
}
/* example */
tcl.resultf("cmd = %s", cmd);
tcl.error("invalid command specified");
注意:调用Tcl::result和Tcl::error有一些细微的区别,就是如果调用result出错,解释器会捕捉到异常,同时给你返回堆栈的追踪,供你调试改正错误,但是如果使用error,是不会返回堆栈的跟踪。
存储并查找TclObjects
NS2在哈希表中存储对编译层次结构中每个TclObject的引用;这允许快速访问对象。
哈希表是解释器的内部。NS2使用TclObject的名称作为键,以在哈希表中输入,查找或删除TclObject。
void Tcl::enter(TclObject* o)
{
int nw;
Tcl_HashEntry* he = Tcl_CreateHashEntry(&objs_, (char*)o->name(),
(int*)&nw);
Tcl_SetHashValue(he, (char*)o);
}
/*
* insert a pointer to the TclObject into hashtable
*/
TclObject* Tcl::lookup(const char* name)
{
/*XXX use tcl hash table */
Tcl_HashEntry* he = Tcl_FindHashEntry(&objs_, (char*)name);
if (he != 0)
return ((TclObject*)Tcl_GetHashValue(he));
return (0);
}
/*
* retrieve the TclObject with the name
*/
void Tcl::remove(TclObject* o)
{
Tcl_HashEntry* he = Tcl_FindHashEntry(&objs_, (char*)o->name());
if (he == 0)
abort();
Tcl_DeleteHashEntry(he);
}
/*
* delete references to the TclObject from the hash table
*/
这些函数由类TclObject和类TclClass在内部使用。
获得对解释器的直接访问
如果上面的方法不够,那么我们必须获取解释器的句柄,并编写我们自己的函数。
inline Tcl_Interp* interp() const { return (tcl_); }
/*
* returns the handle to the interpreter that is stored within the class Tcl.
*/