0

次を使用して 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 プロシージャを単独で呼び出すことができます。

誰がそれを引き起こしているのか知っていますか?名前空間が提供するカプセル化がとても気に入っています。

4

3 に答える 3

1

私には問題ないようです。

Tcl_Eval を実行するために、次の 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");
}

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 を実行することにより、イベント (ファイルイベントまたはコマンドの後に必要) および対話型シェルを処理するための完全な機能が得られます。その例は、簡単なバージョンに従います。

/* 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

ご迷惑をおかけして申し訳ありません。問題を見つけました。問題は私のコードにあります。私は完全に忘れていた次の手順を持っていました:

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 に答える
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 パッケージを作成している場合は、これを強くお勧めします。たとえそこまで進んでいないとしても、良い考えです。スクリプトがどのコンテキストで実行されるかを確実に把握することはできませんsourcenamespace evalnamespace eval

于 2012-09-15T07:35:14.420 に答える