【问题标题】:Is there a way to handle proc that does not exist in Tcl?有没有办法处理 Tcl 中不存在的 proc?
【发布时间】:2014-03-29 02:47:11
【问题描述】:

我是 tcl 的新手。有没有一种方法可以处理虽然在脚本中被调用但不存在的 procs。我给你举个例子

假设这是我们的代码

package require 65ABC
package provide package1
65ABC::callingFunction param1 param2  (# this does not exist in 65ABC)

It will throw an error saying undefined command 65ABC::callingFunction

如果我没记错的话,我不确定TCL,但在其他语言中,在这些情况下有一个universal proc。这意味着在上面提到的场景中,当调用函数不存在时,它将转到某个通用 proc 并执行通用 proc 中编写的操作。假设我们可以打印一条消息说“此 proc 不存在”,或者我们可以执行一些其他操作。

更新:添加catch 命令不是一个选项。因为我大约有 200 个这样的 procs。我想通过一个超级进程来处理这些场景

【问题讨论】:

    标签: tcl


    【解决方案1】:

    您正在寻找的是unknown 和/或namespace unknown 命令。这些命令记录在这里:

    这是unknown 的一个简单示例:

    % rename unknown _unknown ;# save reference to the original
    % proc unknown {args} {
        puts stderr "Hey, I don't know this command: $args"
        uplevel 1 [list _unknown {*}$args]
    }
    % hello world
    Hey, I don't know this command: hello world
    invalid command name "hello"
    

    当然,你可以在这个过程中做任何你想做的事情——你可以记录信息、动态创建缺失的过程、引发错误等等。

    【讨论】:

    • 我们需要在哪里写未知的proc。我们可以在包中写 65ABC。 65ABC 是我们正在搜索 proc 的包,比如 65ABC::testProc
    • 我可以在带有命名空间的包中写这个而不是在脚本中编写未知的过程,比如65ABC::unknown。我需要这样的解决方案
    • namespace unknown 不工作。如果我正在写 proc unknown 它正在工作。现在的问题是它正在修改每个错误并调用unknown proc。假设如果代码有问题,它会直接转到unknown proc 并屏蔽错误
    【解决方案2】:

    你可以使用catch:

    package require 65ABC
    package provide package1
    if {[catch {65ABC::callingFunction param1 param2} err]} {
        puts "Error: $err"
    }
    

    或者try...on error:

    package require 65ABC
    package provide package1
    
    try {
        65ABC::callingFunction param1 param2
    } on error {result options} {
        puts "Something's not so right..."
        return -options $options $result
    }
    

    【讨论】:

    • @Nitesh 你是什么意思?
    • 问题是我有大约 200 个这样的 procs。我不能为每个 proc 设置 catch。如果我得到类似universal proc的东西,那么我只需要添加一次消息
    • 你明白我想说的吗。还是我应该详细解释一下?
    • 你知道通用的意思是如果 TCL 没有找到在 proc 中调用的 proc,那么它将重定向到通用 proc
    • @Nitesh 不建议这样做,但您也可以将所有内容包装在一个 catch 中。这就像你从一个脚本中 source-ing 并包装 source: if {[catch {source scripts.tcl} err]} {...}
    【解决方案3】:

    似乎原始发帖人 (OP) 只对处理名称空间 65ABC 中的未知过程感兴趣。如果是这种情况,那么proc unknown 必须更具选择性:它必须确定未知过程属于哪个名称空间。如果是65ABC,那我们自己处理。否则,我们将让系统处理它。这是我的想法:

    rename unknown original_unknown; # Save the original unknown 
    proc getns {name {defaultNS "::"}} {
        set lastSeparatorPosition [string last "::" $name]
        if {$lastSeparatorPosition == -1} {
            return $defaultNS
        } else {
            incr lastSeparatorPosition -1
            return [string range $name 0 $lastSeparatorPosition]
        }
    }   
    proc unknown {args} {
        set procName [lindex $args 0]
        set ns [getns $procName]
        puts "$args, happens in name space -- $ns"
    
        if {$ns == "::65ABC" || $ns == "65ABC"} {
            puts "  We will handle this ourselves"
        } else {
            uplevel 1 [list original_unknown {*}$args]
        }
    }
    
    # Test it out
    namespace eval ::65ABC {
        proc foo {args} { puts "::65ABC::foo $args" }
    
        # This will never get called
        proc unknown {args} {
            puts "::65ABC::unknown -- $args"
        }
    }
    
    ::65ABC::bar hello there; # We will handle this case
    65ABC::foobar 1 2 3;      # Also handle this case
    foo bar;                  # The system will handle this case
    

    输出

    ::65ABC::bar hello there, happens in name space -- ::65ABC
      We will handle this ourselves
    65ABC::foobar 1 2 3, happens in name space -- 65ABC
      We will handle this ourselves
    foo bar, happens in name space -- ::
    invalid command name "foo"
        while executing
    "original_unknown foo bar"
        ("uplevel" body line 1)
        invoked from within
    "uplevel 1 [list original_unknown {*}$args]"
        (procedure "::unknown" line 12)
        invoked from within
    "foo bar"
        (file "/Users/haiv/Dropbox/src/tcl/unknown_proc.tcl" line 47)
    

    讨论

    • 我拥有的 proc unknown 与 Bryan Oakley 的基本相同,但有一些额外的代码来确定未知 proc 属于哪个名称空间。
    • 为了确定未知过程的名称空间,我创建了proc gents,它返回最后一个"::" 之前的文本。如果 proc 名称不包含 "::",则 gents 将为全局命名空间返回 "::"
    • 在命名空间65ABC 中编写unknown proc 不起作用。我试过了。

    【讨论】:

    • getns 可以替换为内置的namespace qualifiers 命令
    【解决方案4】:

    这只是 Bryan Oakley 答案的扩展。您可以使用 tcl 的自省来查看调用者并采取适当的操作。任何不是源自 B:: 命名空间的调用都将照常处理。

    rename unknown _unknown
    proc unknown {args} {
      array set i [info frame 1]
      # if the uknown command is in the B:: namespace, use our own
      # handler, otherwise, run the default.
      if { [regexp {^B::} $i(cmd)] } {
        puts "unk: $args"
      } else {
        uplevel 1 [list _unknown {*}$args]
      }
    }
    
    namespace eval B {
      proc x {} {
        puts "B::x"
      }
    }
    
    B::x
    B::y
    xyzzy
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-25
      相关资源
      最近更新 更多