I haven't seen this mentioned in the github issues or in the googleverse, so I assume that it isn't some kind of deficiency.
If I have a table that is a field of an object, how do I release the resources being held by that table in the object's destructor?
import tables
type AObj = object
name: string
tableField: Table[string, string]
proc `=destroy`(x: AObj) =
echo "Destroying A named ", x.name
`=destroy`(x.name)
# `=destroy`(x.tableField) # Option 1 - Compile error, no =destroy hook for Table
# clear(x.tableField) # Option 2 - Compile error, x is not var
Thanks for your quick response.
The table, once it is no longer referenced by anything, will destroy itself and call the destructors for all the elements in it.
Unfortunately the example below disagrees:
import tables
type AObj = object
name: string
proc `=destroy`(x: AObj) =
echo "Destroying A named ", x.name
`=destroy`(x.name)
type BObj = object
name: string
tableField: Table[string, AObj]
proc `=destroy`(x: BObj) =
echo "Destroying B named ", x.name
`=destroy`(x.name)
proc newBObj(name: string): ref BObj =
result = BObj.new()
result.name = name
result.tableField["A#1"] = AObj(name: "A#1")
echo "Creating B#1"
var bobj1 = newBObj("B#1")
echo "Setting B#1 to NIL"
bobj1 = nil
echo "... Done"
The output below shows that the destructor for A#1 doesn't get called when B#1 is destroyed, and hence when the table is supposed to be destroyed. The output also shows another surprise:
Creating B#1
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Destroying A named
Setting B#1 to NIL
Destroying B named B#1
... Done
I haven't looked into it yet, but my guess is that the multiple lines with Destroying A named come from the seq inside the Table being initialized - that the variable used to initialize the AObj field in each table entry goes out of scope and causes the AObj destructor to be called.
Your workaround fixed the problem. Thanks very much!
For completeness, the modified code is below:
import tables
type AObj = object
name: string
proc `=destroy`(x: AObj) =
echo "Destroying A named ", x.name
`=destroy`(x.name)
type BObj = object
name: string
tableField: Table[string, AObj]
proc `=destroy`(x: BObj) =
echo "Destroying B named ", x.name
`=destroy`(x.name)
`=destroy`(x.tableField.addr[]) # Workaround for lack of =destroy in Table
proc newBObj(name: string): ref BObj =
result = BObj.new()
result.name = name
result.tableField["A#1"] = AObj(name: "A#1")
echo "Creating B#1"
var bobj1 = newBObj("B#1")
echo "Setting B#1 to NIL"
bobj1 = nil
echo "... Done"
The output, shortened for brevity:
Creating B#1
Destroying A named
<...>
Destroying A named
Setting B#1 to NIL
Destroying B named B#1
Destroying A named
<...>
estroying A named
Destroying A named A#1
Destroying A named
<...>
Destroying A named
... Done
I'm looking into this issue. There is another workaround using var T destructors:
proc `=destroy`(x: var BObj) =
echo "Destroying B named ", x.name
`=destroy`(x.name)
`=destroy`(x.tableField) # Workaround for lack of =destroy in Table