`array nextelement`-当searchId无效时无法捕获错误

时间:2018-11-20 02:56:44

标签: tcl

我一直experimenting with namespace ensembles,并且正在练习实现array foreach(是的,我知道TIP 421)。

我正在使用数组startsearch / anymore / nextelement / donesearch。我现在正在尝试捕获在搜索过程中添加或删除数组元素时引发的错误:我发现我无法在代码中捕获该错误 ,但是我可以捕获它在交互式会话中。

这是一个互动环节:

% array set Y {foo bar baz qux}
% set sid [array startsearch Y]
s-1-Y
% set key [array nextelement Y $sid]
foo
% set Y(hello) world
world
% try {
  set key [array nextelement Y $sid]
} trap {TCL LOOKUP} {msg e} {puts "$sid has been invalidated\n$e"}
s-1-Y has been invalidated
-code 1 -level 0 -errorstack {INNER {invokeStk1 ::tcl::array::nextelement Y s-1-Y}} -errorcode {TCL LOOKUP ARRAYSEARCH s-1-Y} -errorinfo {couldn't find search "s-1-Y"
    while executing
"array nextelement Y $sid"
    ("try" body line 1)} -errorline 1
% 

一切都很好。我们可以看到,在进行活动搜索时添加数组元素后,searchId无效:错误代码以{TCL LOOKUP ARRAYSEARCH}

开头

现在,我将其包装在proc中,并安排在启动交互式tclsh时将其来源(有关详细信息,请参见this)。这是我的array foreach处理程序的正文:

% info body ::monkeypatches::array_foreach

    if {[llength $vars] != 2} {
        error {array foreach: "vars" must be a 2 element list}
    }
    lassign $vars keyVar valueVar

    # Using the complicated `upvar 1 $arrayName $arrayName` so that any
    # error messages propagate up with the user's array name
    upvar 1 $arrayName $arrayName  $keyVar    key  $valueVar  value

    set sid [array startsearch $arrayName]
    while {[array anymore $arrayName $sid]} {
        # This doesn't seem to be able to catch the error when the user
        # tries to modify the array during a search. Hmm.
        try {
            set key [array nextelement $arrayName $sid]
        } trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
            return -options $e "detected attempt to modify the array while iterating"
        }
        set value [set "${arrayName}($key)"]
        uplevel 1 $body
    }
    array donesearch $arrayName $sid
    return

当我重现捕获错误的步骤时,发现我的try没有捕获错误:

% array set Y {foo bar baz qux}
% array foreach {key val} Y {set Y(hello) world}
couldn't find search "s-1-Y"

用通用的trap替换特定的on error子句无济于事。都不使用catch代替try

我不明白为什么此proc无法捕获错误。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

如果查看错误跟踪,则会发现错误并非来自预期的位置。 (出于测试目的,我将程序创建为array_foreach。)

% array_foreach {x y} tcl_platform {set tcl_platform(foo) bar;puts $x->$y}
osVersion->16.7.0
couldn't find search "s-1-tcl_platform"
% set errorInfo
couldn't find search "s-1-tcl_platform"
    while executing
"array anymore $arrayName $sid"
    (procedure "array_foreach" line 12)
    invoked from within
"array_foreach {x y} tcl_platform {set tcl_platform(foo) bar;puts $x->$y}"

您已将陷阱放置在array nextelement上,但没有放置在array anymore上,这实际上是检测到问题(并抛出问题)的地方。

我想您最好将循环更改为此:

while true {
    try {
        if {![array anymore $arrayName $sid]} {
            break
        }
        set key [array nextelement $arrayName $sid]
        set value [set ${arrayName}($key)]
    } trap {TCL LOOKUP ARRAYSEARCH} {"" e} {
        return -options $e "detected attempt to modify the array while iterating"
    }
    uplevel 1 $body
}