安達@沖データと申します。 ruby-dev:8341でまつもとさんに、『もちっと考えたら(意訳)』と言われたん で考えてみた結果を報告しますです。 #自慢できるものではないのですが、もしかして誰か期待している人とか、さ #らに発展させてくれる人が居ないとも限らないんで。 長所: 1) マルチスレッド対応。Thread.current['error_dict']で参照する 2) エラーハンドラを内部で定義する場合、スレッドの先頭で、 ErrorDict::at_thread_startupを呼ぶ。 3) at_thread_startupは、Arrayを作るだけで比較的軽い 4) 最初に参照した時に一度だけコピーを行う 5) コピーは、無指定の場合にはメインスレッドから行う 5) コピー元は、指定すれば任意のスレッド固有の辞書から行える 6) コピーは、Hashへの参照だけをコピーするので比較的軽い 7) 各スレッド内部で、文脈に応じて複数のハンドラを使える(save/restore) #まつもとさんの言っていたことはクリアできたかなぁ? 短所: 1) raiseをCレベルで再定義しないと、使い物にならない 2) raiseの定義の中で、ErrorDictやThread.current['error_dict']といった 固有名詞を参照しなければいけないのが、非常に気持悪い 3) 言ってはみたものの、今一つうまい使い方を思い付けない 長所の方はまぁ良いんですが、短所の方は数が少ない割に致命的なものばかり です。私には、とりあえずどーにもなりません。 作ったソースを張り付けておきます。このまま実行すると、最後に(たぶん) ed.rb:213:in `open': No such file or directory - henoheno (Errno::ENOENT) from ed.rb:213 と言われますが、これがraiseをCレベルで再定義する必要性の具体例です。 #スレッドだと自分の親が分からないことが、結構不便ですね。悩みました。 #ついでに、Arrayのinitializeの呪いも忘れていました。newを実行した時だ #けでもinitializeを実行してくれると、私的にはなかなか嬉しいんですが。 -- error_dict.rb #!/usr/local/bin/ruby class DictStack < Array alias get_at [] alias each_dict_ each def dup (deep=false) tmp = Array.new aHash = {} if (deep) each_pair { | key, value | aHash[key] = value.dup } else each_dict { | h | tmp.push(h) } end tmp.push(aHash) tmp end def []= (key, value) self.get_at(-1)[key] = value end def each_dict each_dict_ { | dict | yield dict } end def [] (key) v = nil reverse_each { | dict | break if ((v = dict[key])) } v end def each_key keys = [] each_dict { | dict | keys += dict.keys } keys.uniq! keys.each { | key | yield key } end def each_pair each_key { | key | yield(key, self[key]) } end def freeze each { | h | h.freeze } super.freeze end def size num = 0 each_key { | k | num += 1 } num end def stack_size length end def save push({}) end alias restore pop alias each each_pair end class << DictStack def [](*lst) if ((lst.size % 2) == 1) raise ArgumentError, "Number of arguments shall be even." end dict = DictStack.new (0 .. (lst.size/2)-1).each { |idx| dict[lst[idx*2]] = lst[idx*2+1] } dict end end class ErrorDict < DictStack ErrorDicts = {} attr_accessor :origin_thread def initialize(th='main') @origin_thread = th ErrorDicts[Thread.current] = self super() end def ensure_copy if (@origin_thread) origin = ErrorDicts[@origin_thread] if (origin.type != self.type) raise RuntimeError, "ErrorDict is not properly initialized" end if (origin.size > 0) pop origin.each_dict { | h | push(h) } push({}) end @origin_thread = nil end end def []= (key, value) ensure_copy super(key, value) end def [] (key) ensure_copy super(key) end end class << ErrorDict def at_thread_startup(th='main') dict = ErrorDict.new dict.push({}) dict.origin_thread = th ErrorDict::ErrorDicts[Thread.current] = self Thread.current['error_dict'] = dict end end ErrorDict::at_thread_startup ErrorDict::ErrorDicts['main'] = Thread.current['error_dict'] alias original_raise raise def raise(*args) case args.size when 0; original_raise when 1; original_raise(args[0]) end if (Thread.current['error_dict']) handler = Thread.current['error_dict'][args[0]] else handler = ErrorDict::ErrorDicts['main'][args[0]] end if (!handler) original_raise(args) end if (handler.type == String) raise handler elsif (handler.type == Proc) handler.call(args) else raise ArgumentError, "Invalid error handler: type = #{handler.type}" end end if __FILE__ == $0 dict = Thread.current['error_dict'] dict[Errno::EINVAL] = "default handler(Errno::EINVAL)" dict[Errno::ENOENT] = Proc.new { | *args | args.each { |arg| print "#{arg}\n" } print "no such file/directory\n"; open ("/dev/null") } print "initial entries\n" Thread.current['error_dict'].each { | key, val | print "#{key} = #{val}\n" } th = Thread.start { ErrorDict::at_thread_startup child_dict = Thread.current['error_dict'] child_dict[Errno::ENOENT] = "new handler(Errno::ENOENT)" print "modified entries\n" Thread.current['error_dict'].each { | key, val | print "#{key} = #{val}\n" } Thread.current.exit } th.join print "resumed entries\n" Thread.current['error_dict'].each { | key, val | print "#{key} = #{val}\n" } raise(Errno::ENOENT, "This is O.K.!") f = open ("henoheno") while f.gets print end f.close end -- *------* adachi / okidata.co.jp |人∧鷲| 沖データ 第一研究所 | <女> | 安達 淳 |牛∨獅| *------*