TCL命名空间和堆栈帧之间有什么区别?

时间:2015-01-15 18:22:36

标签: namespaces tcl callstack upvar uplevel

Upvar会在different stack frame中创建指向变量的链接,有时称为call stackdifferent scope

Upvar还用于为全局(或命名空间)变量2创建别名。但是命名空间只能由namespace eval命令创建。 proc命令创建一个新的堆栈帧。

命名空间和调用堆栈似乎是TCL naming context可以改变的两种方式。 Upvar和Uplevel可以在命名空间和调用堆栈上工作。

我做对了吗?我还没有看到调用堆栈和命名空间之间的直接比较,因此我的问题。

1 个答案:

答案 0 :(得分:8)

不,不完全。命名空间和调用框架是非常不同的概念。命名空间是可以消除同义词歧义的名称层次结构。您的程序中可能有三个名为foo的变量,但如果将它们放在不同的名称空间中,它们不会发生冲突。命名空间可用于变量和命令名称。使用namespace eval创建后,在您调用namespace delete之前,始终可以访问命名空间的内容。

调用堆栈是一系列堆栈帧。第一个堆栈帧#0始终存在。无论何时调用命令,都会创建其他堆栈帧(这主要用于用户定义过程的命令,"内置"命令遵循自己的规则)。当命令返回时,它们会再次被销毁。因此,如果您调用命令A,并且A调用命令B,并且B调用命令C,则您有一个如下所示的调用堆栈:

#3 : <C's variables>
#2 : <B's variables>
#1 : <A's variables>
#0 : <global and namespace variables>

除非使用upvar,否则每个堆栈框架都是一个范围,只有在那里创建或导入到其中的变量才能被访问。其他一切都是隐藏的。在大多数编程语言中,可以从内部作用域自动访问外部作用域的名称,例如全局作用域。在Tcl中不是这样。

使用upvar,您可以让命令查看其自己的堆栈帧之外的内容。例如,C可以使用upvar #0 foo bar为全局变量bar创建别名(foo),或者使用upvar 1 baz qux(注意没有#)来创建别名(qux)用于B&#39的堆栈框架中的变量baz

uplevel命令可以沿着相同的行使用,以在另一个堆栈帧中执行脚本,包括全局脚本。在执行期间,脚本可以访问该堆栈帧中的所有内容,但不包括任何其他内容,包括调用uplevel的堆栈帧中的变量。

C还可以使用::abc::def为命名空间变量upvar #0 ::abc::def ghi创建别名,但不要这样做,而是使用namespace upvar ::abc def ghi

您可以使用upvar #0 foo foo导入全局变量,而不是global foo。在命名空间中定义的命令内,variable命令可以导入在同一命名空间中定义的变量。

upvaruplevel对#0(全局帧)或1(呼叫者的帧)通常很有用。使用其他帧编号容易出错,通常表明设计不佳。调用upvar 0 foo bar为同一堆栈帧中的变量(bar)创建别名(foo),这可能非常有用。

正在处理的事件调用的命令使用全局级别在调用堆栈外执行。它们无法进入活动堆栈帧并访问驻留在那里的变量。

一个简单的演示:

namespace eval ::abc {
    variable def 42

    proc xyz {} {
        variable def
    }
}

set foo 1138

proc A {} {
    B
}

proc B {} {
    set baz 1337
    C
}

proc C {} {
    upvar #0 foo bar
    puts $bar
    upvar 1 baz qux
    puts $qux
    namespace upvar ::abc def ghi
    puts $ghi
}