Tcl 从fileevent处理程序操纵非全局变量

Tcl 从fileevent处理程序操纵非全局变量,tcl,Tcl,有没有办法从fileevent处理程序操纵非全局变量?考虑下面的最小服务器: proc initState {stateName} { upvar $stateName state set state(foo) bar set state(baz) bla # ... return } proc handleConnection {stateName newsock clientAddress clientPort} { upvar $state

有没有办法从fileevent处理程序操纵非全局变量?考虑下面的最小服务器:

proc initState {stateName} {
    upvar $stateName state
    set state(foo) bar
    set state(baz) bla
    # ...
    return
}

proc handleConnection {stateName newsock clientAddress clientPort} {
    upvar $stateName state
    fconfigure $newsock -blocking 0
    fconfigure $newsock -buffering line
    fileevent $newsock readable [list handleData $newsock]
    return
}

proc handleData {f} {
    if {[eof $f]} {
        fileevent $f readable {}
        close $f
        return
    }
    gets $f line
    puts $f ok
    # need to modify state here...
    return
}

proc runServer {port} {
    array set state {}
    initState state
    socket -server {handleConnection state} $port
    vwait forever
}

runServer 1234
是否有可能操纵在
runServer
范围内创建的
state
数组,或者是使
state
成为全局变量的唯一方法


我是Tcl的新手,如果我使用C,我只需将指向
state
的指针传递到事件处理程序中,但不幸的是,Tcl不允许这样做。我在这里做了什么奇怪的事情吗?还有更像Tcl的方式吗?

那根本不起作用。问题是Tcl的堆栈帧不能以您想要的方式持久化

解决此问题的标准选项包括:

  • 将状态保留在由“连接令牌”(例如通道名称)索引的全局数组中。记住数组是由字符串索引的;像“
    sock42,hostname
    ”这样的复合键是相当合法的

  • 将状态保留在以连接令牌命名的命名空间中。如果您使用的是Tcl 8.5,那么
    namespace-upvar
    命令会使这变得更容易

  • 将状态保留在TclOO对象中(需要Tcl 8.6或8.5的单独TclOO包),或使用不同的对象系统(例如,[incr Tcl]、XOTcl;这些可用于许多Tcl版本)

  • 将状态保持在一个协同程序中(需要Tcl 8.6)。这有效地为您提供了一个命名堆栈(并允许您编写代码,因此它显然是“直线”的,而不是由回调驱动的),但其版本要求是严格的


  • 这根本行不通。问题是Tcl的堆栈帧不能以您想要的方式持久化

    解决此问题的标准选项包括:

  • 将状态保留在由“连接令牌”(例如通道名称)索引的全局数组中。记住数组是由字符串索引的;像“
    sock42,hostname
    ”这样的复合键是相当合法的

  • 将状态保留在以连接令牌命名的命名空间中。如果您使用的是Tcl 8.5,那么
    namespace-upvar
    命令会使这变得更容易

  • 将状态保留在TclOO对象中(需要Tcl 8.6或8.5的单独TclOO包),或使用不同的对象系统(例如,[incr Tcl]、XOTcl;这些可用于许多Tcl版本)

  • 将状态保持在一个协同程序中(需要Tcl 8.6)。这有效地为您提供了一个命名堆栈(并允许您编写代码,因此它显然是“直线”的,而不是由回调驱动的),但其版本要求是严格的


  • 感谢您的全面解释,为此使用名称空间是一种有趣的可能性。@用户另一种可能性是为每个连接生成一个唯一的令牌名称,并使用
    upvar#0$token ary;使用$ary(这个)和$ary(那个)
    。这就是Tcl的
    http
    包的工作原理。感谢您的全面解释,为此使用名称空间是一种有趣的可能性。@用户另一种可能性是为每个连接生成一个唯一的令牌名称,并使用
    upvar\0$token ary调用全局数组;使用$ary(这个)和$ary(那个)
    。这就是Tcl的
    http
    包的工作原理。