从C执行的代码中的Tcl名称空间

从C执行的代码中的Tcl名称空间,tcl,Tcl,我有一个来自C应用程序的Tcl代码,使用: Tcl_Eval(tcl_interp, "source nmsp.tcl") 一切正常。 但是,名称空间范围没有保留。例如,以下文件: #!/bin/sh # namespace evaluation namespace eval bob { namespace eval joe { proc proc1 {} {} } proc proc2 {} { puts "proc2" }

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

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过程本身可以很好地调用,而无需任何名称空间指定

有人知道是什么引起的吗?我非常喜欢名称空间提供的封装。

对我来说似乎很好

我创建了以下Tcl扩展来执行Tcl评估:

#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");
}
然后用上面发布的内容创建了一个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;
}
更好的嵌入方案是使用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"

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

如果这是问题所在,请将外部
命名空间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"
    }
}

namespace eval::bob{#很抱歉给你们带来麻烦。我发现了问题。
问题出在我的代码中。我完全忘记了以下过程:

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

此过程的目的主要是向我的代码中的所有过程添加入口点和出口点跟踪程序。出于某种原因(我仍在研究),它还删除了名称空间范围。

为什么要检查Tcl 8.4?我不知道-那里的8.4指定了此处所需的Tcl API的最低版本。鉴于该版本已发布很久以前,8.4是我希望处理的最古老的版本。如果我说“8.2”相反,我们会删除一些函数,并且能够使用8.2以上的任何函数。我的做法是相反的。您创建了一个DLL,然后将其加载到tclsh中。我所做的是:我创建了一个c应用程序,加载解释器,然后调用Tcl_Eval(interp,“source test_namespace.Tcl”);不涉及tclsh。我想知道这是否能起到作用。@用户这没关系。当您从C代码执行
Tcl\u Eval
时,它总是使用当前作用域;当您没有做任何更改时,这是全局作用域。
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
namespace eval ::bob {    # <<<<<<< NOTE THIS HERE!
    namespace eval joe {
        proc proc1 {} {}
    }
    proc proc2 {} {
        puts "proc2"
    }
    proc ::proc3 {} {
        puts "proc3"
    }
    proc joe::proc4 {} {
        puts "proc4"
    }
}
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
    }
}