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

Tcl:如何跨命名空间边界进行回调?,tcl,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 { pu

考虑这样一个结构的库:

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 $::user::x }
}
如果我尝试这样使用它,它将不起作用:

....
namespace eval ::user {
    set x 10
    ::mylib::f { puts $x }
}
原因是mylib中不知道$x。我可以这样修复它:

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 $::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和更新版本的解决方案很好;dr版本:
命名空间代码

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

namespace eval ::user {
    variable x 10
    ::mylib::f [namespace code { puts $x }]
}
在内部,它使用
namespace inscope
来实现这一点。建议您不要直接使用它<代码>命名空间代码是执行此操作的方便方法

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

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

(事实上,在8.3中,
eval
是不必要的,因为在
unknown
中有一个严重的黑客攻击,但是请按照我上面推荐的方式进行,因为我们在以后的版本中进行了黑客攻击。这非常糟糕。)

从8.0开始,这里的一切都应该可以工作;从8.0到8.4,名称空间没有任何重大变化……它工作得很好!我只是不明白:如果回调站点传递参数,那么块如何访问这些参数呢?评论我自己的评论:我认为只有在回调变量只包含过程调用的情况下,您将参数从回调端传递到已计算块的技巧才有效,对吗?