I am having a problem/have found a bug with slots created with an -initcmd. If I set a value on an object containing a slot with an initcmd and then move it an error gets thrown every time I read the value.
Class Fred -slots { Attribute a -initcmd { set _ 4 } } Fred x x a 4 x move y y a can't read "a": invalid command name "::x"
Are there any simple work arounds for this other than having to write a method for 'a'?
Neil Hampton www.rostrvm.com
Neil,
Interesting catch (though hairy)!
I am having a problem/have found a bug with slots created with an -initcmd. If I set a value on an object containing a slot with an initcmd and then move it an error gets thrown every time I read the value.
The reason for this erratic behaviour is that the move (as well as the copy) operation does NOT handle the transfer of variable traces properly. Some background: The ::xotcl::Attribute infrastructure uses Tcl variable traces to indirect operations (/obj/ set ...) on object variables to their respective attribute slot objects. When moving an object (x), these traces are supposed to migrate to the new or target object (y).
This migration action, however, is currently (1.6.6) broken:
a) the variable trace commands carry the name of the source object (::x). for now, the occurrences of the source object's name in the command statement are not replaced by the target object's name. hence, the error:
can't read "a": invalid command name "::x"
(as it should by "::y" after the move!)
b) investigating this issue, I found that the migration action of var traces leads to an unwanted accumulation of trace definitions on the respective target objects (e.g., two initcmd scripts being eval'ed on ::y, rather than one!).
i came up with a fix against 1.6.6. as i am not sure whether it will make it into a patch release in this shape (and assuming that you cannot upgrade to such a patch release), i refactored the patch into something ready-made. I tested it with initcmd, valuecmd, and valuechangecmd. there might be a more straight-forward approach for your deployment scenario, however, this one hasn't crossed my mind yet.
namespace eval ::xotcl::unsupported { namespace import ::xotcl::*
Class create Copyable \ -instproc clear_variable_traces {obj var op trace_cmd {max 1}} { set traces [$obj trace info variable $var] set occurrences [llength [lsearch -all -exact $traces [list $op $trace_cmd]]] while {$occurrences > $max} { $obj trace remove variable $var $op $trace_cmd incr occurrences -1 } } -instproc __default_from_cmd {obj cmd var sub op} { set trace_cmd [list [self] [self proc] $obj $cmd] my clear_variable_traces $obj $var $op $trace_cmd 0 next } -instproc __value_from_cmd {obj cmd var sub op} { set trace_cmd [list [self] [self proc] $obj $cmd] my clear_variable_traces $obj $var $op $trace_cmd next } -instproc __value_changed_cmd {obj cmd var sub op} { set trace_cmd [list [self] [self proc] $obj $cmd] my clear_variable_traces $obj $var $op $trace_cmd next }
::xotcl::Attribute instmixin add Copyable
Class create FixedCopyHandler -instproc copyTargets {} { next foreach origin [my set targetList] { set dest [my getDest $origin] foreach var [$dest info vars] { set cmds [$dest trace info variable $var] foreach cmd $cmds { foreach {op def} $cmd break $dest trace remove variable $var $op $def foreach idx [lsearch -exact -all $def $origin] { lset def $idx $dest } $dest trace add variable $var $op $def } } } }
::xotcl::Object::CopyHandler instmixin add FixedCopyHandler }
hope it helps, //stefan
Dear Neil,
many thanks for the (most likely condensed) example. The bug was that the traces from initcmd were handled twice by copy/move, one time correctly, the other time not (Stefan's patch from 2'oclock fixes the second copy and removes the duplicates later, but removes too many traces).
See below for a patch against xotcl 1.6.6. We'll do some more testing, but this or a similar patch will go into the next releases (as well the 2.0 branch).
For now, you might get the fixed version from: git clone git://alice.wu-wien.ac.at/xotcl
best regards -gustaf neumann
=================================================== --- a/generic/predefined.xotcl +++ b/generic/predefined.xotcl @@ -596,9 +596,14 @@ unset p foreach cmd $cmds { foreach {op def} $cmd break #$origin trace remove variable $var $op $def - if {[lindex $def 0] eq $origin} { + set domain [lindex $def 0] + if {$domain eq $origin} { set def [concat $dest [lrange $def 1 end]] } + if {[my isobject $domain] && [$domain istype ::xotcl::Slot]} { + # slot traces are handled already by the slot mechanism + continue + } $dest trace add variable $var $op $def } }