0

我有一个 Tcl 代码,它来自一个 C 应用程序,使用:

Tcl_Eval(tcl_interp, "source nmsp.tcl")

一切运行良好。但是,不会保留命名空间范围。例如,以下文件:

#!/bin/sh

# namespace evaluation
namespace eval bob {
    namespace eval joe {
        proc proc1 {} {}
    }
    proc proc2 {} {
        puts "proc2"
    }
    proc ::proc3 {} {
        puts "proc3"
    }
    proc joe::proc4 {} {
        puts "proc4"
    }
}

puts "Namespace calling [info procs ::bob\::*]"

单独运行时会产生以下输出:

Namespace calling ::bob::proc2

但是当从 Tcl_Eval 采购时不会打印任何东西。事实上,proc2 过程可以在没有任何命名空间指定的情况下自行调用。

有谁知道可能是什么原因造成的?我真的很喜欢命名空间提供的封装。

4

3 回答 3

1

对我来说似乎很好。

我创建了以下 Tcl 扩展来执行您的 Tcl_Eval:

#include <tcl.h>

static int
DotestCmd(ClientData clientData, Tcl_Interp *interp,
          int objc, Tcl_Obj *const objv[])
{
    return Tcl_Eval(interp, "source test_namespace.tcl");
}

int DLLEXPORT
Testnamespace_Init(Tcl_Interp *interp)
{
    if (Tcl_InitStubs(interp, "8.4", 0) == NULL) {
        return TCL_ERROR;
    }
    Tcl_CreateObjCommand(interp, "dotest", DotestCmd, NULL, NULL);
    return Tcl_PkgProvide(interp, "testnamespace", "1.0");
}

在 Windows 上,我使用以下方法编译了它:

cl -nologo -W3 -O2 -MD -DNDEBUG -DUSE_TCL_STUBS -I\opt\tcl\include -c test_namespace.c
link -dll -release -out:testnamespace.dll test_namespace.obj \opt\tcl\lib\tclstub85.lib

然后使用您在上面发布的内容创建了一个 test_namespace.tcl 文件。运行它会产生以下结果:

C:\opt\tcl\src>tclsh
% load testnamespace.dll Testnamespace
% dotest
Namespace calling ::bob::proc2
%

进一步的反省表明事情与我对该脚本的期望一样:

% namespace children ::
::platform ::activestate ::bob ::tcl
% namespace children ::bob
::bob::joe
%

如果这真的不适合你,你可能首先在你的 C 代码中做一些奇怪的事情。

更新

上面的例子是用一个编译的包来扩展 tcl。显然,OP 正在将 Tcl 嵌入到其他一些应用程序中。此处提供了执行此操作的一个简单示例,它也运行相同的命令以达到与上述相同的效果。实际上,当将 Tcl 嵌入到应用程序中时,代码应该使用 tclAppInit.c 文件并提供它自己的 Tcl_AppInit 函数。通过运行通常的 Tcl_Main,您可以获得处理事件(fileevents 或 after 命令所需)和交互式 shell 的全部功能。一个例子如下:

/* trivial embedding Tcl example */
#include <tcl.h>
#include <locale.h>

int
main(int argc, char *argv[])
{
    Tcl_Interp *interp = NULL;
    int r = TCL_ERROR;

    setlocale(LC_ALL, "C");
    interp = Tcl_CreateInterp();
    if (interp != NULL) {
        Tcl_FindExecutable(argv[0]);
        r = Tcl_Eval(interp, "source test_namespace.tcl");
        if (TCL_OK == r)
            r = Tcl_Eval(interp, "puts [namespace children ::bob]");
        Tcl_DeleteInterp(interp);
    }
    return r;
}

运行上述:

C:\opt\tcl\src>cl -nologo -W3 -O2 -MD -I\opt\tcl\include test_namesp_embed.c -link -subsystem:console -release -libpath:\opt\tcl\lib tcl85.lib
test_namesp_embed.c

C:\opt\tcl\src>test_namesp_embed.exe test_namespace.tcl
Namespace calling ::bob::proc2
::bob::joe

一个更好的嵌入方案,它使用 tclAppInit 来扩展一个普通的 Tcl 解释器:

#include <tcl.h>
#include <locale.h>

#define TCL_LOCAL_APPINIT Custom_AppInit
int
Custom_AppInit(Tcl_Interp *interp)
{
    return Tcl_Eval(interp, "source test_namespace.tcl");
}

#include "/opt/tcl/src/tcl.git/win/tclAppInit.c"

构建和运行它也会产生与以前版本相同的输出:

C:\opt\tcl\src>cl -nologo -W3 -O2 -MD -I\opt\tcl\include test_namesp_embed.c -link -subsystem:console -release -libpath:\opt\tcl\lib tcl85.lib
C:\opt\tcl\src>test_namesp_embed.exe
Namespace calling ::bob::proc2
% namespace children ::bob
::bob::joe
% exit
于 2012-09-14T22:19:24.953 回答
0

据我所知,您的代码无法生成您期望的消息的唯一方法是,如果当前命名空间在它被调用时不是全局命名空间。假设当前命名空间是::foo,第一个命名空间namespace eval将在其中工作,::foo::bob内部命名空间在::foo::bob::joe. 当然,不合格的过程定义会将其定义放在当前名称空间中。要检测是否确实如此,请将namespace current命令的输出添加到打印的消息中。

如果这是问题所在,请将外部更改namespace eval为使用完全限定名称:

namespace eval ::bob {    # <<<<<<< NOTE THIS HERE!
    namespace eval joe {
        proc proc1 {} {}
    }
    proc proc2 {} {
        puts "proc2"
    }
    proc ::proc3 {} {
        puts "proc3"
    }
    proc joe::proc4 {} {
        puts "proc4"
    }
}

如果您正在编写一个 Tcl 包,强烈建议您这样做,即使您没有完全做到这一点,这也是一个好主意;您永远无法确定脚本将在什么上下文source中运行。(但内部namespace eval还可以;它在已知上下文中运行,外部namespace eval.)

于 2012-09-15T07:35:14.420 回答
0

麻烦各位了。我发现了问题。问题出在我的代码中。我完全忘记了以下程序:

rename proc _proc
_proc proc {name args body} {
    global pass_log_trace

    set g_log_trace "0"
    if {[info exists pass_log_trace]} {
        set g_log_trace $pass_log_trace
    }

    # simple check if we have double declaration of the same procedure
    if {[info procs $name] != ""} {
        puts "\nERROR: redeclaration of procedure: $name"
    }

    _proc $name $args $body

    if {$g_log_trace != 0} {
        trace add execution $name enter trace_report_enter
        trace add execution $name leave trace_report_leave
    }
}

这个过程的主要目的是为我的代码中的所有过程添加入口和出口点跟踪器。出于某种原因,我仍在调查,它还删除了命名空间范围。

于 2012-09-17T22:28:29.450 回答