Tcl:如何跨命名空间边界进行回调?

时间:2014-09-25 13:15:44

标签: tcl

考虑一个如下结构的库:

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 }
}

原因是mylib中不知道$ x。我可以像这样修理它:

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和更新版本的解决方案很受欢迎。

1 个答案:

答案 0 :(得分:2)

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不是必需的,但请按我上面建议的方式进行,因为我们采取了在以后的版本中出现问题。这非常糟糕。)