2012-02-22 7 views
2

fileeventハンドラから非グローバル変数を操作する方法はありますか?fileeventハンドラから非グローバル変数を操作する

proc initState {stateName} { 
    upvar $stateName state 
    set state(foo) bar 
    set state(baz) bla 
    # ... 
    return 
} 

proc handleConnection {stateName newsock clientAddress clientPort} { 
    upvar $stateName state 
    fconfigure $newsock -blocking 0 
    fconfigure $newsock -buffering line 
    fileevent $newsock readable [list handleData $newsock] 
    return 
} 

proc handleData {f} { 
    if {[eof $f]} { 
     fileevent $f readable {} 
     close $f 
     return 
    } 
    gets $f line 
    puts $f ok 
    # need to modify state here... 
    return 
} 

proc runServer {port} { 
    array set state {} 
    initState state 
    socket -server {handleConnection state} $port 
    vwait forever 
} 

runServer 1234 

runServerの範囲で作成しstate配列を操作するためのいずれかの可能性があるかstateグローバル変数を作り、これを行うための唯一の方法は次のとおりです。以下の最小限のサーバーを考えてみましょうか?

私はCを使用していたとしても、単にstateへのポインタをイベントハンドラに渡しますが、残念ながらTclはそれを許可しません。私はここで変なことをしていますか?もっとTcl-ishの方法がありますか?

答えて

4

これは単純に機能しません。問題は、Tclのスタックフレームが、あなたが必要とするような形で持続しないことです。

この問題を回避するための標準的なオプションは次のとおり

  1. 「接続トークン」(例えば、チャネルの名前)によって指標付けされグローバル配列状態を保ちます。配列は文字列でインデックス付けされていることに注意してください。 "sock42,hostname"のような複合キーはかなり合法です。

  2. 接続トークンの後に名前が付けられた名前空間に状態を維持します。 Tcl 8.5を使用している場合は、namespace upvarコマンドを使用すると、が多くに簡単になります。

  3. TclOO オブジェクト(TCL 8.6 又は 8.5別個TclOOパッケージを必要とする)状態を維持または、XOTcl [TCL INCR]異なるオブジェクト・システム(例えば、使用;これらは、多くのTclのために利用可能ですバージョン)。

  4. 状態を保つコルーチン(Tcl 8.6が必要です)。これは、名前付きスタックを効果的に提供します(コールバックによって駆動されるのではなく、明らかに "直線"になるようにコードを書くことができます)が、バージョン要件はstrictです。

+0

包括的な説明をいただきありがとうございます。名前空間を使用すると面白い可能性があります。 – user1226159

+0

@user別の方法として、接続ごとに固有のトークン名を生成し、 'upvar#0 $ token ary;を使ってグローバル配列を呼び出すこともできます。 $ ary(これ)と$ ary(それ) 'で何かする。これがTclの 'http'パッケージの仕組みです。 –

関連する問題