On Friday 22 February 2002 21:37, you wrote:
...below is a still simpler version...
[cut]
Ehm, I'd say we'd have to take some more care about encapsulation....
Consider procedure A calling procedure B calling procedure C. Each of them try to create a "private" object "bar" from the "foo" class. What happens?
The object from A gets overwritten from the B and then B gets overwritten from C. That's not good. We have to assure that all created object names are unique, since they are visible from all calling frames.
I've posted the better solution to the mailing list but it somehow never reached the list, so I'll try it again. Below you'll find my last working version. It uses a Tcl array to store variable-objectname bindings so we know which object to destroy when a named variable gets unset. This is needed since variable traces are invoked AFTER the variable has already been deleted. Furthermore, traces are invoked in the calling procedure frame if we have a variable deletion because the procedure is about to exit. This is tricky stuff, but I think i have it under control now. See for yourself... Oh yes, the Tcl array with bindings is kept in the callers frame.
we we can make a bind command, where we can bind an object to a variable in the sense that
a) the object is deleted, when the variable is unset b) the object can provide a print-value, when someone read the value of the variable
this might be useful for e.g. associations as well, where you have an instance variable refering to an object, and once you delete the object (with its variables) you want to destroy the referred object (or decrement a reference counter)
Indeed; "bind" is a very good name for this. We can also serialize the object when somebody does the read on the bound variable. This way one can pass objects per-value across function calls and also over remote procedure calls. Speaking of rpc, I have developed Tcl-like rcp using the XOTcl, AOLserver and http as transport so I can test the idea...
PS: i have a little bad feeling about filters/mixins and uplevel/upvar and friends.
Huh, let me think deeply about that...
Now the code... I changed "private" to "volatile" to emphasise the fact.
#------- CUT HERE -------
# # Gets invoked from variable unset callbacks. # Class proc __gc {n1 n2 op} { set l [::expr {[::info level] > 0}] if {![::uplevel $l ::info exists __gc($n1)]} { ::incr l } ::uplevel $l $__gc($n1) destroy }
# # Used to instantiate "volatile" objects. # These get auto-destroyed when the procedure goes out of context. # Class instproc volatile {n args} { ::upvar $n v ::trace variable v u [list Class __gc] ::set v [[self] autoname -instance [self]] ::uplevel [::expr {[::info level]<2?1:2}] ::set __gc($n) $v ::eval [self] create $v $args }
# # Test routines # proc aa {} { _foo_ volatile obj $obj test bb } proc bb {} { _foo_ volatile obj $obj test cc } proc cc {} { _foo_ volatile obj $obj test unset obj dd } proc dd {} { _foo_ volatile obj $obj test } proc gctest {} { Class _foo_ _foo_ instproc test {} {puts stderr "proc [info level -1] object [self]"} set cmds [info comm _foo_*] aa if {[info comm _foo_*] == $cmds} { puts stderr ok } else { puts stderr fail } }
#------- CUT HERE -------