> Thanks I've actually learned a bit here too.

Well, my learning curve was somewhat steeper, thank you again. And if I 
understood the background sufficiently well, I might have found a reasonable 
solution for my issue that involves the lessons learned.

For me, it looks sufficiently foolproof, although it involves (a pretty basic) 
`=destroy`. It keeps the `cached` children as ref objects (yes, far better) 
and, well, seems to do the trick required ...
    
    
    import
      tables
    
    type
      SomeEnum = enum
        PARENT, CHILD
      RefSomeOne = ref SomeOne
      SomeOne = object
        case kind: SomeEnum
        of PARENT:
          children: Table[int, RefSomeOne]
        of CHILD:
          id: int
          parent: RefSomeOne
      ChildErasor = object
        id: int
        parent: RefSomeOne
    
    proc `=destroy`(x: var ChildErasor) =
      # The foolproof part:
      if not x.parent.isNil():
        var child: RefSomeOne
        if x.parent.children.pop(x.id, child):
          echo "erase child" & $x.id
      # ------------------------------------------------
      # the really important piece (NEVER, NEVER, EVER FORGET!):
        x.parent = nil
    
    proc getChild(parent: RefSomeOne): tuple[child: RefSomeOne, erasor: 
ChildErasor] =
      let
        child_id = parent.children.len() + 1
        child = RefSomeOne(kind: CHILD, id: child_id, parent: parent)
        erasor = ChildErasor(id: child_id, parent: parent)
      parent.children[child_id] = child
      result.child = child
      result.erasor = erasor
    
    var
      parent = RefSomeOne(kind: PARENT)
      child_1 = parent.getChild()
    discard child_1
    block:
      var child_2  = parent.getChild()
      discard child_2
      doAssert parent.children.len() == 2
      block:
        var child_3  = parent.getChild()
        discard child_3
        doAssert parent.children.len() == 3
      doAssert parent.children.len() == 2
    doAssert parent.children.len() == 1
    
    
    Run

Output:
    
    
    [Allocated] 0000000000670040 result: 0000000000670050
    [Allocated] 0000000000670070 result: 0000000000670080
    [Allocated] 00000000006700A0 result: 00000000006700B0
    [Allocated] 00000000006700D0 result: 00000000006700E0
    erase child3
    [Freed] 00000000006700D0
    erase child2
    [Freed] 00000000006700A0
    erase child1
    [Freed] 0000000000670070
    [Freed] 0000000000670040

Have also thought about sending Andreas lots of chocolates, sweets and flowers 
for him to invoke some `at_destroy(x: var T)` (`= default`, if not overloaded) 
at the very beginning of nim's standard `=destroy` (no clue how 
costly/possible): It's not too uncommon that something is to be done by 
standard before destroying object instances (cleanup, tasks etc.). Although it 
might add some flexibility, avoid repetitive code and be even more foolproof, I 
think I can live with the above for the moment. Sorry, Andreas, for the 
chocolate & flowers. 

Reply via email to