View · Search · Index
No registered users in community xowiki
in last 10 minutes

Re: [Xotcl] poll for opinions

From: Zoran Vasiljevic <zoran_at_archiware.com>
Date: Sat, 23 Feb 2002 18:47:27 +0100

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 -------