I wrote some container type which keep objects. I want to ensure that objects will be deleted if they are not longer needed.
I am able to generate objects inside my class and I can access these objects inside the class and also delete/destruct them.
But I can not access the objects from outside scope. What must be added to get access the elements?
#!/usr/bin/wish
package require itcl
namespace import itcl::*
class A {
private variable x
public constructor { x_ } {
set x $x_
puts "Create A"
puts "this $this with value $x"
}
public method Do { } {
puts "A::Do called"
puts "Value x $x"
}
# Attention constructor did not have braces for parms even not empty ones!
public destructor {
puts "destruct insance of A, value of x $x"
}
# Delete my self
public method delete { } {
puts "delete object $this"
# why we need to add :: to delete here
::delete object $this
}
}
class OwningList {
private variable objects
public constructor { } {
set objects {} ; # create empty list
}
public method Generate { classname args } {
set object [ $classname #auto $args ]
lappend objects $object
}
# attention: No braces for paremeters in destructor
destructor {
foreach el $objects { ::delete object $el }
}
# using default parameter for parameter 2
public method EraseByIndex { start_ { end_ -1 } } {
if { $end_ == -1 } {
::delete object [lindex $objects $start_]
set objects [lreplace $objects $start_ $start_]
} else {
for { set s $start_ } { $s <= $end_ } { incr s } {
::delete object [lindex $objects $s]
}
set objects [lreplace $objects $start_ $end_]
}
}
public method GetList { } {
return [itcl::scope objects]
}
public method Do { } {
foreach n $objects {
puts "Call function from class scope"
$n Do ; # works!
}
}
public method CallAFunction { object function args } {
puts "Execute: $object $function $args"
eval $object $function $args
}
}
OwningList ol;
ol Generate A 0
ol Generate A 1
ol Generate A 2
ol Generate A 3
ol Generate A 4
ol Generate A 5
ol Generate A 6
puts "Erase 5"
ol EraseByIndex 5
puts "erase 1 to 2"
ol EraseByIndex 1 2
ol Do
set x [ ol GetList ]
upvar 0 $x y
foreach n $y {
puts "Got $n"
puts "Want to do: $n Do"
#$n Do ; # <<<<<<<<<<<<<<<<<<<<<<<<<<<< this will not work! Error message see below
ol CallAFunction $n Do
}
puts "Erase Container"
delete object ol
Error in startup script: invalid command name "a0"
while executing
"$n Do"
("foreach" body line 4)
invoked from within
"foreach n $y {
puts "Got $n"
puts "Want to do: $n Do"
$n Do
ol CallAFunction $n Do
}"
(file "./owning_list.tcl" line 100)
How can I fix this issue?
The problem is that you're iterating over a list of names that are local to the container's context. This is perhaps exacerbated by the fact that your GetList
method returns the handle to the variable; while useful in some circumstances (attaching traces, debugging, etc.) that's probably not a good idea; it's typically not a good idea for things "outside" an object to manipulate the internal state of an object, at least as a design principle.
What I'd do is alter GetList
to return the contents of the list, processed so that each object is represented by its fully-qualified name. Probably this:
public method GetList {} {
return [lmap item $objects { namespace which $item }]
}
Then I'd iterate over them from outside like this:
foreach item [o1 GetList] {
$item Do
}