From: Christoph (news_chr) Date: 2003-07-10T08:57:44+09:00 Subject: Re: Marshaling objects partially "Tanaka Akira" wrote in message news:87znjnvl2e.fsf@serein.a02.aist.go.jp... > In article <200307091409.h69E9Qn21340@moulon.inra.fr>, > ts writes: > > > I must say that I've not understood > > > svg% cat b.rb > > This works well because an object is allocated before child object is > loaded as: > > > obj = self.new > > @@loaded.push(obj) > > obj.ref = Marshal.load(str).shift > > But marshal.c cannot know an object allocated by C._load before > calling C._load. if I run Guys example through a fairly well tested (- the thread part;-) "Compare by Value" module --- module CompareByValue def ==(other) if not instance_of?(other.class) false elsif equal?(other) true elsif id < other.id CompareByValue.comp?(self,other) else CompareByValue.comp?(other,self) end end end class << CompareByValue # Seen is a book keeping class for currently # active thread local CompareByValue comparisons. class Seen < Hash # Helper class to store Id Pairs. const_set :Pair, Struct.new(:l,:r) def initialize(l,r) store(Pair.new(l,r),true) end def default(key) store(key,true) nil end def seen?(l,r) self[Pair.new(l,r)] end def unseen(l,r) delete(Pair.new(l,r)) end end # symbol mangling const_set :SEEN_COMPS, "CBV#{id}_comps".intern const_set :NUM_CALLS, "CBV#{id}_calls".intern def comp?(l,r) return true if thread_local_seen?(l,r) begin Thread.current[NUM_CALLS] +=1 return Comp.new.comp?(l,r) rescue Thread.current[SEEN_COMPS].unseen(l,r) raise ensure if (Thread.current[NUM_CALLS] -= 1).zero? Thread.current[SEEN_COMPS].clear end end end def thread_local_seen?(l,r) if comps = Thread.current[SEEN_COMPS] comps.seen?(l,r) else Thread.current[NUM_CALLS] = 0 Thread.current[SEEN_COMPS] = Seen.new(l,r) false end end class Comp < Hash def comp?(l,r) return true if l.equal?(r) vars = l.instance_variables.sort! return false unless vars == r.instance_variables.sort! # store(l.id,r.id) vars.each { |name| ll = l.instance_eval(name) rr = r.instance_eval(name) if CompareByValue === ll return false unless ll.instance_of?(rr.class) if rr_seen_id = self[ll.id] return false unless rr_seen_id == rr.id else return false unless comp?(ll,rr) end else return false unless ll == rr end } return true end end end class C include CompareByValue attr_accessor :ref @@dumped = {} @@loaded = [] @@num = 0 def _dump(limit) if @@dumped[self] "\000#{@@dumped[self]}\000" else @@dumped[self] = @@num @@num += 1 Marshal.dump([@ref], limit-1) end end def self._load(str) if /\A\000(\d+)\000\z/ =~ str obj = @@loaded[$1.to_i] else obj = self.new @@loaded.push(obj) obj.ref = Marshal.load(str).shift end obj end end Kernel.const_set :A, C.new Kernel.const_set :B, C.new A.instance_eval { @ref = B; @xxx = 1 } B.instance_eval { @ref = A; @xxx = 2 } p A p B p a = Marshal.load(Marshal.dump(A)) p b = Marshal.load(Marshal.dump(B)) p B == A # false p b == a # true p B == a # false p b == a.ref # true p B == A.ref # true p B == a.ref # false p b == A.ref # false --- I am getting an output that does not look quite right to me --- #, @xxx=2>, @xxx=1> #, @xxx=1>, @xxx=2> #>> #>> false true false true true false false --- but I am not sure if I would blame this on the current "_dump, _load" scheme. /Christoph