Results 1 to 21 of 21

Thread: Nested Collection Iterations

  1. #1

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Nested Collection Iterations

    Ok, I've got a collection of objects I'm iterating in a loop. Specifically, they're Node objects (TreeView).

    Now, inside my iteration loop, I'd like to pass the whole collection to another procedure, and possibly iterate it down in that other procedure.

    When I return, I want my higher-level iteration to pick up where it left off.

    Is there a problem with this? I'll be running tests while waiting on a reply.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  2. #2

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Hmmm, ok, it seems to work just fine. My test....

    For a quick class (Class1):
    Code:
    
    Option Explicit
    
    Public s As String
    
    
    My test in Form1:
    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        Dim c As New Collection
        Dim o As Class1
    
        Set o = New Class1: o.s = "001": c.Add o
        Set o = New Class1: o.s = "002": c.Add o
        Set o = New Class1: o.s = "003": c.Add o
        Set o = New Class1: o.s = "004": c.Add o
        Set o = New Class1: o.s = "005": c.Add o
    
        For Each o In c
            Debug.Print o.s
            Test c
        Next
    
    
    End Sub
    
    Sub Test(c As Collection)
        Dim o As Class1
    
        For Each o In c
            Debug.Print "     nested: "; o.s
        Next
    
    End Sub
    
    
    And my Immediate Window output:
    Code:
    001
         nested: 001
         nested: 002
         nested: 003
         nested: 004
         nested: 005
    002
         nested: 001
         nested: 002
         nested: 003
         nested: 004
         nested: 005
    003
         nested: 001
         nested: 002
         nested: 003
         nested: 004
         nested: 005
    004
         nested: 001
         nested: 002
         nested: 003
         nested: 004
         nested: 005
    005
         nested: 001
         nested: 002
         nested: 003
         nested: 004
         nested: 005
    Ok, so I guess my question changes to: Am I asking for any trouble when doing this?

    EDIT: I guess it's my lack of full understanding of the (under the hood) stuff of iterations that's got me worried. There's obviously some index-pointer somewhere that's keeping track of where the iteration is. I was just worried about what happens to that pointer when there's a sub-iteration.

    Does it use the actual object (in the example above, the "o" variable) to reference itself and find the next one? That's definitely how a "For i = a to b" loop works. If that's so, I should be able to change the object variable (used for iteration) and make the loop prematurely terminate. I'll test that now.


    .
    Last edited by Elroy; Jan 14th, 2022 at 07:54 PM.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  3. #3

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Nope, it's not using the iteration variable for the pointer. Proof:

    (Same Class1 from above.)

    In a Form1:
    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        Dim c As New Collection
        Dim o1 As Class1
    
        Set o1 = New Class1: o1.s = "001": c.Add o1
        Set o1 = New Class1: o1.s = "002": c.Add o1
        Set o1 = New Class1: o1.s = "003": c.Add o1
        Set o1 = New Class1: o1.s = "004": c.Add o1
        Set o1 = New Class1: o1.s = "005": c.Add o1
    
        ' Ok, right now, o1 is the last item added.
        ' But let's make sure.
        Debug.Print "Right now, o1: "; o1.s
    
    
        Dim o2 As Class1
        For Each o2 In c
            Debug.Print o2.s
            If o2.s = "002" Then Set o2 = o1
        Next
        Debug.Print "dropped out"
    End Sub
    
    
    And my Debug output:
    Code:
    Right now, o1: 005
    001
    002
    003
    004
    005
    dropped out
    If it was using the iterator variable (o2 in this case), then my output should have looked like this (but it didn't):
    Code:
    Right now, o1: 005
    001
    002
    dropped out
    So, I just really don't know how iteration loops work. I know how to use them, but it'd be nice to have a deeper understanding.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  4. #4

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    ..

    I've been thinking about it, and I still don't "get" it. I mean, we're not talking about different instantiations. We're talking about different references to the same object (the collection). And, I thought a reference was nothing more than a four-byte (for 32-bit) pointer (basically an alias) to the actual COM object. So, I don't see how an iteration pointer could be stored with the reference variable.

    Now sure, the COM object has a counter in it that keeps track of how many references there are to it (uninstantiating the COM object when that counter goes to zero). In fact, I'm sure there's lots of header information in these COM object, the vTable, etc, etc. But, there's still just one object and one counter and one header. How can these iteration pointers be in the actual COM object? Is there some array of them in the COM object? Is there a limit to how deeply we can do this nested iterating?

    Hmmm, I guess another possibility is that an iteration loop sets up its own internal (invisible) variable/header and keeps track of things on its own. That almost has to be the answer. I just don't see another reasonable way. But then, that makes me wonder how these iteration loops are cleaned up if/when you jump out of the middle of them. For a typical For i = a To b loop, there's nothing really to clean up.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  5. #5

  6. #6
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Nested Collection Iterations

    Try this
    Code:
            If o2.s = "002" Then
                c.Add o1
            End If
    The VBA.Collection enumerator does see changes to the linked list of the base object.

    It's possible to implement an enumerator which snapshots the linked list initially and is not affected (does not see) any mutation of the base container (e.g. ADODB.Fields -- not sure actually, just a possibility).

    In every For Each statement there is an unsung hero being the enumerator object which is completely missing from public typelibs and is known in the world only through its IEnumVARIANT interface :-))

    cheers,
    </wqw>

  7. #7

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Thank you, Trick & Wqweto. That explains it. It seems my last "guess" was fairly close, "another possibility is that an iteration loop sets up its own internal (invisible) variable/header and keeps track of things on its own". And with y'all's information, I can go study the IEnumVARIANT interface and DISPID_NEWENUM when/if I want to know more.

    This also explains why I can delete items in a For Each loop and it doesn't get confused.

    Wqweto, that "snapshot" explanation may come in very handy in the future.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  8. #8
    PowerPoster Arnoutdv's Avatar
    Join Date
    Oct 2013
    Posts
    5,872

    Re: Nested Collection Iterations

    My, in the meanwhile, quite old code for a hierarchical tree structure.
    Nothing fancy, does it's job ;-)

    The Tree class
    Code:
    '---------------------------------------------------------------------------------------
    ' Module    : clsTree
    ' DateTime  : 25-10-2006
    ' Author    : ArnoutV
    ' Purpose   :
    '---------------------------------------------------------------------------------------
    Option Explicit
    
    Private m_cNodes As Collection
    Private m_cKeys As Collection
    Private m_lKeyCnt As Long
    
    '---------------------------------------------------------------------------------------
    ' Procedure : Add
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Adds a treenode to the tree
    '           : ParentKey and Key can be empty
    '---------------------------------------------------------------------------------------
    Public Function Add(sParentKey As String, sKey As String, sText As String) As clsTreeNode
    
      Dim myNode As clsTreeNode
      Dim myParent As clsTreeNode
      
      If Exists(sKey) Then Exit Function
      If Len(sParentKey) > 0 Then If Not Exists(sParentKey) Then Exit Function
      
      m_lKeyCnt = m_lKeyCnt + 1
      
      Set myNode = New clsTreeNode
      myNode.Key = sKey
      myNode.Text = sText
      myNode.UniqueKey = "key:" & m_lKeyCnt
      
      If Len(sParentKey) > 0 Then
        Set myParent = m_cNodes(m_cKeys(sParentKey))
        ' Add as child to parent
        myParent.AddChild myNode
        ' Set current parent of child
        Set myNode.Parent = myParent
      End If
      
      m_cNodes.Add myNode, myNode.UniqueKey
      
      If Len(sKey) > 0 Then
        m_cKeys.Add myNode.UniqueKey, sKey
      End If
      
      Set Add = myNode
      Set myNode = Nothing
    End Function
    
    '---------------------------------------------------------------------------------------
    ' Procedure : TreeNode
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Returns the TreeNode or Nothing, based on the Key
    '---------------------------------------------------------------------------------------
    Public Property Get TreeNode(sKey As String) As clsTreeNode
      Set TreeNode = pTreeNode(sKey)
    End Property
    
    '---------------------------------------------------------------------------------------
    ' Procedure : TreeNodeIndex
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Returns the TreeNode or Nothing, based on the index
    '---------------------------------------------------------------------------------------
    Public Property Get TreeNodeIndex(ByVal lIndex As Long) As clsTreeNode
      If lIndex > 0 And lIndex <= m_cNodes.Count Then
        Set TreeNodeIndex = m_cNodes(lIndex)
      End If
    End Property
    
    '---------------------------------------------------------------------------------------
    ' Procedure : Exists
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Returns whether a node for a given key exists in the tree
    '---------------------------------------------------------------------------------------
    Public Function Exists(sKey As String) As Boolean
      If Len(sKey) > 0 Then
        Exists = Not (pTreeNode(sKey) Is Nothing)
      End If
    End Function
    
    '---------------------------------------------------------------------------------------
    ' Procedure : Count
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Returns the number of nodes in the tree
    '---------------------------------------------------------------------------------------
    Public Function Count() As Long
      Count = m_cNodes.Count
    End Function
    
    '---------------------------------------------------------------------------------------
    ' Procedure : Remove
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Removes a node (complete branch) from the tree for a given node key
    '---------------------------------------------------------------------------------------
    Public Sub Remove(sKey As String)
      Dim myNode As clsTreeNode
      
      Set myNode = pTreeNode(sKey)
      
      If Not myNode Is Nothing Then pRemove myNode.UniqueKey
      Set myNode = Nothing
    End Sub
    
    '---------------------------------------------------------------------------------------
    ' Procedure : Clear
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Empties the tree
    '---------------------------------------------------------------------------------------
    Public Sub Clear()
      Dim myNode As clsTreeNode
      
      Do While m_cNodes.Count > 0
        For Each myNode In m_cNodes
          If myNode.IsTopLevelNode Then
            pRemove myNode.UniqueKey
            Exit For
          End If
        Next
      Loop
      
      Set myNode = Nothing
    End Sub
    
    '---------------------------------------------------------------------------------------
    ' Procedure : DrawTree
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Returns an ASCII representation of a (branch of the) tree
    '---------------------------------------------------------------------------------------
    Public Function DrawTree(Optional sKey As String, Optional iStartIndentLevel As Integer = 0) As String
      Dim myNode As clsTreeNode
      Dim sTree As String
      
      If Len(sKey) = 0 Then
        For Each myNode In m_cNodes
          If myNode.IsTopLevelNode Then
            sTree = myNode.DrawTree(iStartIndentLevel)
            If Len(DrawTree) = 0 Then
              DrawTree = sTree
            Else
              DrawTree = DrawTree & vbCrLf & sTree
            End If
          End If
        Next
      Else
        Set myNode = pTreeNode(sKey)
        If Not myNode Is Nothing Then
          DrawTree = myNode.DrawTree(iStartIndentLevel)
        End If
      End If
      
    End Function
    
    '---------------------------------------------------------------------------------------
    ' Procedure : NewEnum
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : To iterate through all nodes using a For..Each loop
    '---------------------------------------------------------------------------------------
    Public Property Get NewEnum() As IUnknown
      Set NewEnum = m_cNodes.[_NewEnum]
    End Property
    
    '---------------------------------------------------------------------------------------
    ' Procedure : pTreeNode
    ' DateTime  : 25-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Returns either the treenode or nothing for a given key
    '---------------------------------------------------------------------------------------
    Private Function pTreeNode(sKey As String) As clsTreeNode
      Dim sUniqueKey As String
      
      On Error GoTo pTreeNode_Error
    
      sUniqueKey = m_cKeys(sKey)
      Set pTreeNode = m_cNodes(sUniqueKey)
      Exit Function
    
    pTreeNode_Error:
      Set pTreeNode = Nothing
      
    End Function
    
    '---------------------------------------------------------------------------------------
    ' Procedure : pRemove
    ' DateTime  : 30-10-2006
    ' Author    : ArnoutV
    ' Purpose   : Internal removal routine based on unique key
    '---------------------------------------------------------------------------------------
    Private Sub pRemove(sUniqueKey As String)
      Dim myNode As clsTreeNode
      Dim cChildNode As clsTreeNode
      Dim cParentNode As clsTreeNode
      
      Set myNode = m_cNodes(sUniqueKey)
      If myNode.HasChildren Then
        For Each cChildNode In myNode.Children
          pRemove cChildNode.UniqueKey
        Next
      End If
      
      Set cParentNode = myNode.Parent
      If Not cParentNode Is Nothing Then cParentNode.RemoveChild myNode.Key
      
      If Len(myNode.Key) > 0 Then m_cKeys.Remove myNode.Key
      m_cNodes.Remove sUniqueKey
      
      Set myNode = Nothing
    End Sub
    
    Private Sub Class_Initialize()
      Set m_cNodes = New Collection
      Set m_cKeys = New Collection
      m_lKeyCnt = 0
    End Sub
    
    Private Sub Class_Terminate()
      Clear
      Set m_cNodes = Nothing
      Set m_cKeys = Nothing
    End Sub

    The TreeNode class
    Code:
    '---------------------------------------------------------------------------------------
    ' Module    : clsTreeNode
    ' DateTime  : 25-10-2006
    ' Author    : ArnoutV
    ' Purpose   :
    '---------------------------------------------------------------------------------------
    Option Explicit
    
    Private m_cParent As clsTreeNode
    Private m_cChildNodes As Collection
    
    Private m_sUniqueKey As String
    Private m_sKey As String
    Private m_sText As String
    Private m_sTag As String
    Private m_bHidden As Boolean
    
    Public Property Let UniqueKey(ByVal sValue As String)
      m_sUniqueKey = sValue
    End Property
    
    Public Property Get UniqueKey() As String
      UniqueKey = m_sUniqueKey
    End Property
    
    Public Property Let Key(ByVal sValue As String)
      m_sKey = sValue
    End Property
    
    Public Property Get Key() As String
      Key = m_sKey
    End Property
    
    Public Property Let Text(ByVal sValue As String)
      m_sText = sValue
    End Property
    
    Public Property Get Text() As String
      Text = m_sText
    End Property
    
    Public Property Let Hidden(ByVal bValue As Boolean)
      m_bHidden = bValue
    End Property
    
    Public Property Get Hidden() As Boolean
      Hidden = m_bHidden
    End Property
    
    Public Property Let Tag(ByVal sValue As String)
      m_sTag = sValue
    End Property
    
    Public Property Get Tag() As String
      Tag = m_sTag
    End Property
    
    Public Property Set Parent(cValue As clsTreeNode)
      Set m_cParent = Nothing
      Set m_cParent = cValue
    End Property
    
    Public Property Get Parent() As clsTreeNode
      Set Parent = m_cParent
    End Property
    
    Public Sub AddChild(cNode As clsTreeNode)
      If Len(cNode.Key) Then
        m_cChildNodes.Add cNode, cNode.Key
      Else
        m_cChildNodes.Add cNode
      End If
    End Sub
    
    Public Sub RemoveChild(sKey As String)
      On Error Resume Next
      If Len(sKey) > 0 Then m_cChildNodes.Remove sKey
    End Sub
    
    Public Function Child(sKey As String) As clsTreeNode
      Set Child = pTreeNode(sKey)
    End Function
    
    Public Function ChildIndex(ByVal lIndex As Long) As clsTreeNode
      If lIndex > 0 And lIndex <= m_cChildNodes.Count Then
        Set ChildIndex = m_cChildNodes(lIndex)
      End If
    End Function
    
    Public Function IsTopLevelNode() As Boolean
      IsTopLevelNode = (m_cParent Is Nothing)
    End Function
    
    Public Function IndentLevel() As Long
      Dim cParent As clsTreeNode
      
      Set cParent = m_cParent
      
      Do Until cParent Is Nothing
        IndentLevel = IndentLevel + 1
        Set cParent = cParent.Parent
      Loop
      
      Set cParent = Nothing
    End Function
    
    Public Function TextPath(Optional sSeparator As String = vbTab) As String
      Dim cParent As clsTreeNode
      
      TextPath = m_sText
      Set cParent = m_cParent
      
      Do Until cParent Is Nothing
        TextPath = cParent.Text & sSeparator & TextPath
        Set cParent = cParent.Parent
        If Not cParent Is Nothing Then
          If cParent.Hidden Then Set cParent = Nothing
        End If
      Loop
      
      Set cParent = Nothing
    End Function
    
    Public Function KeyPath(Optional sSeparator As String = vbTab) As String
      Dim cParent As clsTreeNode
      Dim sKey As String
      
      sKey = m_sKey
      If sKey = "" Then sKey = m_sText
      
      KeyPath = sKey
      Set cParent = m_cParent
      
      Do Until cParent Is Nothing
        sKey = cParent.Key
        If Len(sKey) = 0 Then sKey = cParent.Text
        KeyPath = sKey & sSeparator & KeyPath
        Set cParent = cParent.Parent
        If Not cParent Is Nothing Then
          If cParent.Hidden Then Set cParent = Nothing
        End If
      Loop
      
      Set cParent = Nothing
    End Function
    
    Public Function HasChildren() As Boolean
      HasChildren = m_cChildNodes.Count > 0
    End Function
    
    Public Function NofChildren() As Long
      NofChildren = m_cChildNodes.Count
    End Function
    
    Public Function Children() As Collection
      Set Children = m_cChildNodes
    End Function
    
    Public Function ChildKeys() As String()
      Dim aKeys() As String
      Dim cTreeNode As clsTreeNode
      Dim lCnt As Long
      
      If m_cChildNodes.Count > 0 Then
        ReDim aKeys(m_cChildNodes.Count - 1)
        For Each cTreeNode In m_cChildNodes
          aKeys(lCnt) = cTreeNode.Key
          lCnt = lCnt + 1
        Next
      End If
      
      ChildKeys = aKeys
    End Function
    
    Public Function DrawTree(Optional ByVal iIndentLevel As Long = 0) As String
      Dim cChildNode As clsTreeNode
      
      If iIndentLevel > 0 Then DrawTree = String(iIndentLevel, vbTab)
      DrawTree = DrawTree & m_sText
      
      If m_cChildNodes.Count > 0 Then
        For Each cChildNode In m_cChildNodes
          If Not cChildNode.Hidden Then
            DrawTree = DrawTree & vbCrLf & cChildNode.DrawTree(iIndentLevel + 1)
          End If
        Next cChildNode
      End If
    End Function
    
    Public Property Get NewEnum() As IUnknown
      'this property allows you to enumerate
      'this collection with the For...Each syntax
      Set NewEnum = m_cChildNodes.[_NewEnum]
    End Property
    
    '---------------------------------------------------------------------------------------
    ' Procedure : pTreeNode
    ' DateTime  : 25-10-2006
    ' Author    : ArnoutV
    ' Purpose   :
    '---------------------------------------------------------------------------------------
    Private Function pTreeNode(sKey As String) As clsTreeNode
      On Error GoTo pTreeNode_Error
    
      Set pTreeNode = m_cChildNodes(sKey)
      Exit Function
    
    pTreeNode_Error:
      Set pTreeNode = Nothing
      
    End Function
    
    Private Sub Class_Initialize()
      Set m_cChildNodes = New Collection
      Set m_cParent = Nothing
    End Sub
    
    Private Sub Class_Terminate()
      Set m_cChildNodes = Nothing
      Set m_cParent = Nothing
    End Sub

  9. #9
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: Nested Collection Iterations

    I know you don't like me bringing up VB.Net in your threads but for the purposes of this specific topic, I cannot avoid it since For...Each works the same way in both VB6 and VB.Net. However, enumerators are exposed in .Net but not in VB6 so I cannot demonstrate what For...Each actually does using VB6 code.

    When the compiler encounters a For...Each like this:-
    Code:
            Dim myArray = {1, 2, 3, 4, 5}
    
            For Each i In myArray
                Debug.WriteLine(i)
            Next
    It turns it into this:-
    Code:
            Dim myArray = {1, 2, 3, 4, 5}
    
            'Creates an enumerator object. IEnumerator is the .Net equivalent
            'of VB6's IEnumVARIANT interface
            Dim enumerator As IEnumerator = myArray.GetEnumerator
    
            'Loop through each item using the IEnumerator
            'interface
            Do While enumerator.MoveNext
                Debug.WriteLine(enumerator.Current)
            Loop
    You can even create multiple enumerator objects to iterate the same list and they can all be in different positions. Example:-
    Code:
            Dim myArray = {1, 2, 3, 4, 5}
            Dim enumerator = myArray.GetEnumerator()
            Dim enumerator2 = myArray.GetEnumerator
    
            'Skip two items on the second enumerator
            enumerator2.MoveNext()
            enumerator2.MoveNext()
    
            Debug.WriteLine("First enumerator:")
    
            Do While enumerator.MoveNext
                Debug.WriteLine(enumerator.Current)
            Loop
    
            Debug.WriteLine("Second enumerator:")
    
            Do While enumerator2.MoveNext
                Debug.WriteLine(enumerator2.Current)
            Loop
    Output:-
    Code:
    First enumerator:
    1
    2
    3
    4
    5
    Second enumerator:
    3
    4
    5
    As you can see from the above, both enumerators work independently of each other.

    Again, I apologize for annoying you yet again with more .Net stuff but I could not demonstrate the concept using VB6 since this is all hidden there. But the concept is exactly the same. For...Each works the exact same way in both languages with some nuanced differences that are not relevant to the overall concept.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  10. #10

  11. #11

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Quote Originally Posted by The trick View Post
    Niya, VB6 doesn't use IEnumVariant for iterating through arrays it just uses index.
    Hi Trick. I assume you're talking about arrays of intrinsic variables, excluding Variants (that is, String, Byte, Boolean, Integer, Long, Single, Double, Currency, Date). Yeah, we've got no way to truly "iterate" those without an explicit For index = a To b loop (or wrapping them in objects).

    However, we can iterate a Variant loop. And, nesting seems to work just fine:

    Form1:
    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        Dim va() As Variant
        ReDim va(4)
        va(0) = 100
        va(1) = 101
        va(2) = 102
        va(3) = 103
        va(4) = 104
    
        Dim v As Variant
        For Each v In va
            Debug.Print v
            NestTest va
        Next
    
    End Sub
    
    Sub NestTest(va() As Variant)
        Dim v As Variant
        For Each v In va
            Debug.Print "    Nested: "; v
        Next
    End Sub
    
    
    Output:
    Code:
     100 
        Nested:  100 
        Nested:  101 
        Nested:  102 
        Nested:  103 
        Nested:  104 
     101 
        Nested:  100 
        Nested:  101 
        Nested:  102 
        Nested:  103 
        Nested:  104 
     102 
        Nested:  100 
        Nested:  101 
        Nested:  102 
        Nested:  103 
        Nested:  104 
     103 
        Nested:  100 
        Nested:  101 
        Nested:  102 
        Nested:  103 
        Nested:  104 
     104 
        Nested:  100 
        Nested:  101 
        Nested:  102 
        Nested:  103 
        Nested:  104
    So, I assume these Variant array iteration loops are using IEnumVariant.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  12. #12

  13. #13

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Quote Originally Posted by The trick View Post
    Elroy, i meant what you are talking about:
    Code:
    Dim v As Variant
    
    For Each v In Array(1, 2, 3, 4, 5, 6)
        Debug.Print v
    Next
    This for-each loop doesn't use IEnumVariant but a hidden index variable.
    Hmmm, ok. Good to know.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  14. #14

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Ok, I've got another related question.

    When iterating a Collection, we've established that it uses an IEnumVariant mechanism to keep track of iterations. However, we've still got several ways to get out of these iteration loops (Exit For, Exit Do, Exit Sub/Function, GoTo).

    And, I'm assuming that, in all those cases, the loop somehow cleans up the IEnumVariant mechanism? Correct? It's a bit curious to me how it does that, as something like a GoTo is a pretty raw exit.

    As an analogy, I'm comfortable with VB6 cleaning up object variables (and arrays, and strings) when they fall out of scope. And, VB6 sort of "cheats" this by not having variables more specific than procedures, so that's all easier to keep track of. However, the IEnumVariant mechanism is more specific.

    (Also, I guess the same question applies to a variant array enumeration. Even if it doesn't use IEnumVariant, it's still got to clean up its internal index variable when we exit the loop.)

    Also, I'm now curious about what happens if/when we GoTo into the middle of one of these loops. I'll test and see.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  15. #15

  16. #16

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    I figured it out.

    These IEnumVariant variables and the Variant Array index variables are also procedure scoped variables. As such, they get cleaned up when the procedure exits (just like all other procedure scoped variables). So, on Exit For, Exit Do, & GoTo, they don't get cleaned up. But on Exit Sub & Exit Function, they do get cleaned up.

    EDIT: I suppose I should say "procedure lifetime" rather than "procedure scoped", so that any Static variables aren't confused. Although, these IEnumVariant and Variant Array index variables almost certainly aren't static, so scope and lifetime is the same.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  17. #17

  18. #18

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Quote Originally Posted by The trick View Post
    When you use Exit For statement it immediately cleans-up the enumerator reference. When you use GoTo statement the enumerator is released when the function exits. An index variable isn't freed because it's just an integer value.
    Ahhh, I can test that.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  19. #19

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    9,852

    Re: Nested Collection Iterations

    Ahh, Trick, you are correct as usual.

    Code:
    
    Option Explicit
    
    Private Sub Form_Load()
        Dim c As New Collection
    
        c.Add 100
        c.Add 101
        c.Add 102
        c.Add 103
        c.Add 104
    
        Dim v As Variant
        For Each v In c
            Debug.Print v
            If v = 102 Then Exit For
    label1:
        Next
        If v = 104 Then Exit Sub
        GoTo label1
    
    End Sub
    
    
    Output:
    Code:
     100 
     101 
     102
    Termination:
    Name:  Loop.png
Views: 278
Size:  4.3 KB

    ... on the "Next" statement.
    Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.

  20. #20

  21. #21
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: Nested Collection Iterations

    Quote Originally Posted by The trick View Post
    Niya, VB6 doesn't use IEnumVariant for iterating through arrays it just uses index.
    I'm aware there are differences. I was focused on this:-
    For each" cycle for objects is based on the IEnumVariant interface. So when you initiate a loop the runtime requests for an IEnumVariant enumerator object (through DISPID_NEWENUM request). This object usually is a different object which has its own state like the current item etc.
    I wasn't too focused on the details. But good info nonetheless.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width