3

Tcl8.6のTclOOとRivetを少し試していますが、やりたいことができないので困っています。

.rvtこの問題は、ファイル内の次のコードで簡単に再現できます。

<?

proc dumbproc {} {
    puts "this is dumbproc ([namespace current])"
}

oo::class create foo {
    method bar {} {
        puts "this is bar ([namespace current])"
        dumbproc
    }
}

set obj [foo new]

dumbproc

$obj bar

コードを単純に見ると、期待どおりに機能するはずですが、Rivetパッケージの微妙な動作と選択された特定の構成のため、実際には機能しません。

この例では、コードが名前空間.rvt内で実行されるファイルを使用しているため、プロシージャの完全修飾名はです。名前解決アルゴリズムがメソッド内で呼び出されると、メソッド内を検索し、次に中、最後に内を検索しますが、それを見つけて次のエラーを出すことはありません。::requestdumbproc::request::dumbprocbardumbproc::oo::Obj12::oo::

this is dumbproc (::request) this is bar (::oo::Obj16)

invalid command name "dumbproc"
    while executing
"dumbproc"
    (class "::request::foo" method "bar" line 3)
    invoked from within
"$obj bar"
    (in namespace eval "::request" script line 21)
    invoked from within
"namespace eval request {
puts -nonewline ""


    proc dumbproc {} {
        puts "this is dumbproc ([namespace current])"
    }

    oo::class create..."

ですから、Tclはそれが行うこと、つまり機能を実行するのに正しいのです。ただし、クラスコードを作成するときは、それが使用されるコンテキストを知っている必要があるため、動作は予測できません。

<?実際、最初のリベットマジックをドロップし、コードをtest.tclファイル内に配置してインタラクティブセッションで使用すると、同じエラーが発生します。

$ tclsh
% namespace eval ::myns {source test.tcl}
this is dumbproc (::myns)
this is bar (::oo::Obj12)
invalid command name "dumbproc"

クラス作成コードの前に現在の名前空間を追加して問題を解決しようとしました

::oo::class create [namespace current]::foo { ... }

obj次に、名前空間内にオブジェクトを作成しようとしました

::oo::class create [namespace current]::foo { ... }
namespace eval [namespace current] {set obj [[namespace current]::foo new]}

create次に、名前空間を含む修飾名をオブジェクトに付けるために、クラスのメソッドに切り替えました。

foo create [namespace current]::obj
obj bar

しかし、すべてが失敗しました。すべての試行は、私がそれをどのように行っても、TclOOクラス内のメソッドは常にそのオブジェクトの一意の名前空間内で実行されることを示しています。私が間違っている?

私が欲しいものを手に入れる方法はありますか?TclOOはそのように機能することを意図していませんか?この場合、なぜですか?私が本当に驚いたのは、このコンテキスト依存の動作です。これが正しいかどうかはわかりませんが、完全に間違っている可能性があり、それに対する適切なケースがあります。

4

1 に答える 1

5

各TclOOオブジェクトの内部は、実際には独自の名前空間です。self namespaceメソッド内またはnamespace currentメソッド内で、名前空間の名前を取得したり、どこからでも名前空間を取得したりできますinfo object namespace $theobj。名前空間にデフォルトで配置される唯一のコマンドはmy(プライベートメソッドを呼び出すため)であり、他の名前空間の一部のコマンドは、標準のTclnamespace pathメカニズムを介して利用可能になります(これが取得selfおよびnext利用可能です)。

これを修正する最も簡単な方法は、おそらくこれをfooクラスのコンストラクターに追加することです。

namespace path [list {*}[namespace path] ::request]

特定のケースでは、実際にコンストラクターを追加する必要があります...

constructor {} {
    namespace path [list {*}[namespace path] ::request]
    # If you had a non-trivial constructor in a superclass, you'd need to call
    # [next] too.
}

長期的には、クラスのオブジェクトのデフォルトを作成するために使用される名前空間のリストに追加するためのメカニズムを要求することは合理的かもしれません。必要に応じて、機能リクエストを送信してください…</ p>


[編集]:現在のオブジェクトのコマンド解決パスに親名前空間を追加した直後の場合は、もう少し魔法を追加することでそれを行うことができます。

oo::class create foo {
    self {
        method create args {
            set ns [uplevel 1 {namespace current}]
            next {*}[linsert $args 1 $ns]
        }
        method new args {
            set ns [uplevel 1 {namespace current}]
            next {*}[linsert $args 0 $ns]
        }
    }
    constructor {creatorNS args} {
        namespace path [list {*}[namespace path] $creatorNS]
    }
    method bar {} {
        puts "this is bar ([namespace current])"
        dumbproc
    }
}

これにより、作成時に現在の名前空間がインスタンスのパスに自動的に配置されます。多くのクラスでこれを行う場合は、大部分の機械を含むメタクラスを作成することをお勧めしますが、上記の手法(クラスオブジェクト自体に対するいくつかのメソッドのself 宣言foo)は、単純な場合にはうまく機能します。

于 2012-11-08T11:24:27.853 に答える