当通道事件调用过程并检索指针指向的值时使用 upvar?

Using upvar when procedures are invoked by channel events and retrieving the value to which a pointer points?

我对下面的代码有两个问题。

  1. 是否可以在ReadLine中调用通道事件时使用upvar?在试验 upvar 时,我使用了完全相同的场景,只是 ReadLine 是直接调用的,而不是通过事件调用的;它工作正常。它失败了,因为 state($sock) 没有被识别。是需要一个不同的 #n 值还是必须是全局值?它尝试了 #2,但抛出了一个错误值。

  2. 在过程 ReadLine 中,我试图创建一个指向 $state($sock) 的指针,使其指向列表本身。例如,在实验中,它对 lappend $sptr $op 运行良好;但在 set row [lindex $sptr 0] 上失败了,因为 $sptrstate(1)。但是,如果使用 set,它确实有效,例如 set row [lindex [set $sptr] 0]。为什么是这样?是否有另一种获取指针指向的值的方法?尝试在 Tcl 中使用指针是个坏主意吗?过程体被转换为字节码表示的事实是否意味着使用指针没有太大区别?

谢谢。

proc ClientConnect {sock client_ip client_port} {    
   if {![info exists state($sock)]} {set state($sock) {1}}; 
   chan configure $sock -buffering line -blocking 0 -translation crlf
   chan event $sock readable [list ReadLine $sock]
}

proc ReadLine {sock} {

  # The #1 in upvar is to first caller, which is ClientConnect.
  # Make a ptr to the array list rather than retrieving it each reference,
  # such that $sptr is state($sock)'s list.

  upvar #1 state r
  set sptr r($sock)
  puts stdout "x: [lindex $r($sock) 0]"; #Fails to recognize state.
  set row [lindex [set $sptr] 0]
}

似乎正常工作的实验代码:

proc ReadLine {sock} {
 upvar #1 row rtemp; #Recognizes row.
 set r rtemp($sock)
 lappend $r "new"

 set line "Content-length: 247899"
 if { [string first "Content-length:" $line] == 0 } { lappend $r [string trim [string range $line 16 end]] }

 lappend $r "help"
 puts [lindex [set $r] 3]; #Output is: 247899.
 puts $rtemp($sock); #Output is: 1 GET new 247899 help.
}
    
proc do {sock} {
  set row($sock) {1 GET}
  ReadLine $sock
}

set sock 1
do $sock

如果需要,您可以使用 upvar,但您会发现回调上方唯一可见的堆栈框架是全局堆栈框架。就好像它是用 uplevel #0 调用的一样。它看不到内部过程,如果您仔细考虑一下,原因很明显:无法保证调用发生时任何特定过程都会在堆栈上。

如果您需要将状态从安装回调的位置传递到调用回调的位置,最简单的方法是使用协程(可以从实际的全局回调中恢复)或使用 TclOO对象(当然可以封装各种复杂状态)。在 8.5 及之前的版本中,您受到更多限制并且必须使用 global/namespace 变量,这可能会变得更加混乱。


要使您的变量访问有效,请使用 upvar 为变量创建别名:

# upvar 0 is special in that it aliases a currently-visible variable
upvar 0 row($sock) r
puts [lindex $r 0]

唯一真正的限制是不能将数组放在数组中。很久以前就被删除了,因为它很难使用并且容易导致崩溃。