【问题标题】:Tcl: How to do a callback accross namespace borders?Tcl:如何跨命名空间边界进行回调?
【发布时间】:2014-09-25 13:15:43
【问题描述】:

考虑一个结构如下的库:

package provide ::mylib 1.0

namespace eval ::mylib {
  namespace export f
  proc f { action } {
    # pass action around
    g $action
  }
  proc g { action } {
    eval $action
  }
}

如果我尝试这样使用它,它将无法工作:

....
namespace eval ::user {
    set x 10
    ::mylib::f { puts $x }
}

原因是 $x 在 mylib 中是未知的。我可以这样修复它:

namespace eval ::user {
  set x 10
  ::mylib::f { puts $::user::x }
}

这可行,但是限定 ::mylib::f 参数中的每个变量很尴尬。另一种可能性是将代码包装在另一个命名空间 eval 中:

namespace eval ::user {
    set x 10
    ::mylib::f { namespace eval { puts $x } }
}

更好,但仍然丑陋。如果是 Ruby 或 Perl,我只需将一个闭包传递给 ::mylib::f。 Tcl 的最佳实践是什么?

顺便说一句,我目前正在使用 Tcl 8.3,但希望有可能尽快升级到更新版本,因此欢迎使用 8.3 和更新版本的解决方案。

【问题讨论】:

    标签: tcl


    【解决方案1】:

    tl;dr 版本:namespace code

    namespace code 命令适用于这种情况。您将要封装的脚本传递给它,它会将一个具有魔力的版本返回给您,以便它可以处理从任何地方调用的问题。以下是您可以如何使用它。

    namespace eval ::user {
        variable x 10
        ::mylib::f [namespace code { puts $x }]
    }
    

    在内部,它使用namespace inscope 来执行此操作。建议您不要直接使用它; namespace code 是这样做的便捷方式。

    请注意,即使回调站点将参数传递给它,它也可以工作,只要该站点正在执行:

    eval $callback [list "the argument is this"]
    

    (事实上,在 8.3 中,eval 不是必需的,因为在 unknown 中存在严重 hack,但请按照我上面推荐的方式进行操作,因为我们已将那个 hack 删除后来的版本。真是太糟糕了。)

    【讨论】:

    • 这里的一切都应该从 8.0 开始工作;从 8.0 到 8.4,命名空间没有任何重大变化……
    • 效果很好!我只是不明白:如果回调站点传递参数,块如何访问这些参数?
    • 评论我自己的评论:我认为你将参数从回调端传递到评估块的技巧只有在回调变量只包含过程调用时才有效,对吧?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    • 2021-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多