Results 1 to 22 of 22

Thread: [RESOLVED] Create an object array with a length of zero (or UBound = -1)

  1. #1

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Resolved [RESOLVED] Create an object array with a length of zero (or UBound = -1)

    I'd like to know how to create an object array with a length of zero (or UBound = -1). For example:
    Code:
    Dim arrMyCalss() As MyClass
    
    arrMyCalss = CreateEmptyArray("MyClass")
    
    Debug.Assert UBound(arrMyCalss) = -1
    
    '--- OR ---
    
    Dim arrObject() As Object
    
    arrObject= CreateEmptyArray(vbObject)
    
    Debug.Assert UBound(arrObject) = -1
    Thanks.

  2. #2
    PowerPoster Zvoni's Avatar
    Join Date
    Sep 2012
    Location
    To the moon and then left
    Posts
    3,249

    Re: Create an object array with a length of zero (or UBound = -1)

    Checking against UBound = -1 is nonsense, since UBound(array) = -1 is a valid upper Bound for e.g. Dim MyArray(-5 To -1) As SomeType

    An Array with Length=0 IS "Dim MyArray() As SomeType", since it's not allocating any memory for the Array (-Members), except assigning a memory-address for the variable itself.
    You can use VarPtr-API to check the SAFEARRAY-Structure for number of Dims
    One System to rule them all, One Code to find them,
    One IDE to bring them all, and to the Framework bind them,
    in the Land of Redmond, where the Windows lie
    ---------------------------------------------------------------------------------
    People call me crazy because i'm jumping out of perfectly fine airplanes.
    ---------------------------------------------------------------------------------
    Code is like a joke: If you have to explain it, it's bad

  3. #3
    PowerPoster
    Join Date
    Feb 2006
    Posts
    23,556

    Re: Create an object array with a length of zero (or UBound = -1)

    The test for an empty array is Ubound() < LBound(), and something entirely different from an unallocated dynamic array.
    Last edited by dilettante; Jun 28th, 2022 at 02:19 AM.

  4. #4
    PowerPoster
    Join Date
    Feb 2006
    Posts
    23,556

    Re: Create an object array with a length of zero (or UBound = -1)

    SomeClass.cls:
    Code:
    Option Explicit
    
    Public L As Long
    Module1.bas:
    Code:
    Option Explicit
    
    Private Declare Function SomeClassArray Lib "oleaut32" Alias "SafeArrayCreateVector" ( _
        Optional ByVal VT As VbVarType = vbObject, _
        Optional ByVal LB As Long = 0, _
        Optional ByVal cElements As Long = 0) As SomeClass()
    
    Private Sub Main()
        Dim Ary() As SomeClass
    
        Ary = SomeClassArray()
        MsgBox UBound(Ary) < LBound(Ary)
        ReDim Ary(0)
        Set Ary(0) = New SomeClass
        Ary(0).L = 15
        MsgBox Ary(0).L
    End Sub

  5. #5
    Hyperactive Member
    Join Date
    Mar 2019
    Posts
    290

    Re: Create an object array with a length of zero (or UBound = -1)

    It might seem like a silly question but what problem are you trying to solve? This tells you if an array is empty (not dimensioned) without any exceptions or API calls.

    Code:
    If Not Not ArrayName then
    
       arrayIsEmpty = false
    
    else
    
       arrayIsNotEmpty = true
    
    end if

  6. #6

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by Zvoni View Post
    Checking against UBound = -1 is nonsense, since UBound(array) = -1 is a valid upper Bound for e.g. Dim MyArray(-5 To -1) As SomeType

    An Array with Length=0 IS "Dim MyArray() As SomeType", since it's not allocating any memory for the Array (-Members), except assigning a memory-address for the variable itself.
    You can use VarPtr-API to check the SAFEARRAY-Structure for number of Dims
    Thank you, Zvoni.

    Quote Originally Posted by dilettante View Post
    The test for an empty array is Ubound() < LBound(), and something entirely different from an unallocated dynamic array.
    Yes

    Quote Originally Posted by vbwins View Post
    It might seem like a silly question but what problem are you trying to solve? This tells you if an array is empty (not dimensioned) without any exceptions or API calls.

    Code:
    If Not Not ArrayName then
    
       arrayIsEmpty = false
    
    else
    
       arrayIsNotEmpty = true
    
    end if
    I don't seem to understand what you mean yet.

  7. #7

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by dilettante View Post
    SomeClass.cls:
    Code:
    Option Explicit
    
    Public L As Long
    Module1.bas:
    Code:
    Option Explicit
    
    Private Declare Function SomeClassArray Lib "oleaut32" Alias "SafeArrayCreateVector" ( _
        Optional ByVal VT As VbVarType = vbObject, _
        Optional ByVal LB As Long = 0, _
        Optional ByVal cElements As Long = 0) As SomeClass()
    
    Private Sub Main()
        Dim Ary() As SomeClass
    
        Ary = SomeClassArray()
        MsgBox UBound(Ary) < LBound(Ary)
        ReDim Ary(0)
        Set Ary(0) = New SomeClass
        Ary(0).L = 15
        MsgBox Ary(0).L
    End Sub
    Excellent, that's exactly what I wanted. I'd like to further dig its value.

    I want to assign "Dim arrObject(3) As Object" to "Dim arrMyClass() As MyClass" so that arrMyClass becomes an array with 4 Nothing elements.

    That is, I want to initialize MyClass-Array via Object-Array. I'm wondering if there is a way to achieve this. Thanks.

    Code:
    Option Explicit
    
    Private Declare Function SomeClassArray Lib "oleaut32" Alias "SafeArrayCreateVector" ( _
        Optional ByVal VT As VbVarType = vbObject, _
        Optional ByVal LB As Long = 0, _
        Optional ByVal cElements As Long = 0) As SomeClass()
    Private Declare Function SomeObjectArray Lib "oleaut32" Alias "SafeArrayCreateVector" ( _
        Optional ByVal VT As VbVarType = vbObject, _
        Optional ByVal LB As Long = 0, _
        Optional ByVal cElements As Long = 0) As Object()
    
    Private Sub Main()
        Dim arrSomeClass() As SomeClass
        Dim arrObject() As Object
        
        arrSomeClass = SomeClassArray()
        arrObject = SomeObjectArray(vbObject, 0, 3)
        
        arrSomeClass = arrObject
        
    End Sub
    Last edited by SearchingDataOnly; Jun 28th, 2022 at 07:42 AM.

  8. #8
    Addicted Member
    Join Date
    Oct 2011
    Posts
    179

    Re: Create an object array with a length of zero (or UBound = -1)

    ----
    Last edited by argen; Jun 28th, 2022 at 07:55 AM.

  9. #9

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Re: Create an object array with a length of zero (or UBound = -1)

    Hi argen, thank you for your reply.

    dilettante has solved my problem in post#1. My new question is in post#7.

  10. #10
    PowerPoster
    Join Date
    Jun 2013
    Posts
    6,122

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by SearchingDataOnly View Post
    Code:
     
    Private Sub Main()
        Dim arrSomeClass() As SomeClass
        Dim arrObject() As Object
        
        arrSomeClass = SomeClassArray()
        arrObject = SomeObjectArray(vbObject, 0, 3)
        
        arrSomeClass = arrObject
        
    End Sub
    In case this request is only, because you want the initialization to UBound=-1 more generically
    (working with any simple type as Object(), SomeClass(), String(), Long(), etc.) -
    then the following might be of use:

    Code:
    Option Explicit
     
    Private Declare Sub Assign Lib "kernel32" Alias "RtlMoveMemory" (Dst As Any, Src As Any, Optional ByVal CB& = 4)
    Private Declare Function SafeArrayAllocDescriptor Lib "oleaut32" (ByVal Dims As Long, ByVal pSA As Long) As Long
     
    Private Sub Main()
        Dim Arr() As String
     
        InitEmptyArray Arr '<- works generically also for Object(), String(), Long() etc.
        Debug.Print LBound(Arr), UBound(Arr), UBound(Arr) - LBound(Arr) + 1, TypeName(Arr)
        
        ReDim Preserve Arr(0 To 3): Arr(0) = 123 '<- test, whether cbElements was set properly
        Debug.Print LBound(Arr), UBound(Arr), UBound(Arr) - LBound(Arr) + 1, Arr(0)
    End Sub
    
    Public Sub InitEmptyArray(Arr, Optional ByVal Dimensions& = 1)
      If IsArray(Arr) Then Erase Arr Else Exit Sub
      Dim pSA As Long: Assign pSA, ByVal VarPtr(Arr) + 8
      If pSA Then SafeArrayAllocDescriptor Dimensions, pSA Else Exit Sub
      
      Dim SA As Long: Assign SA, ByVal pSA: If SA = 0 Then Exit Sub
      Select Case VarType(Arr) And Not 8192
          Case vbByte:                       Assign ByVal SA + 4, 1&
          Case vbInteger, vbBoolean:         Assign ByVal SA + 4, 2&
          Case vbVariant, vbDecimal:         Assign ByVal SA + 4, 16&
          Case vbDouble, vbDate, vbCurrency: Assign ByVal SA + 4, 8&
          Case Else:                         Assign ByVal SA + 4, 4&
      End Select
    End Sub
    HTH

    Olaf
    Last edited by Schmidt; Jun 28th, 2022 at 02:38 PM.

  11. #11
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    4,048

    Re: Create an object array with a length of zero (or UBound = -1)

    Tweak the SafeArrayAllocDescriptor API declare and can be used without wrapping the SA pointer into a Variant like this

    Code:
    Option Explicit
    
    Private Declare Function SafeArrayAllocDescriptor Lib "oleaut32" (ByVal Dims As Long, pSA() As Any) As Long
    
    Private Type MyUdt
        ID As Long
    End Type
    
    Private Sub Form_Load()
        Dim arrObject()     As Object
        Dim arrUdt()        As MyUdt
        
        Call SafeArrayAllocDescriptor(1, arrObject)
        Debug.Print UBound(arrObject)
        
        '--- works with UDTs too
        Call SafeArrayAllocDescriptor(1, arrUdt)
        Debug.Print UBound(arrUdt)
    End Sub
    Edit: Ooops, there is a problem with using empty UDT array like the one above that we have already discussed here where there are some more ideas too.

    cheers,
    </wqw>

  12. #12
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    7,930

    Re: Create an object array with a length of zero (or UBound = -1)

    I do this all the time. In fact, I've gotten to where I virtually never return undimensioned arrays from functions that return arrays. I'd much rather have one of these 0 to -1 arrays so I can put it straight into a loop (that wouldn't execute).

    Code:
    
    Option Explicit
    
    
    Private Declare Sub SafeArrayAllocDescriptor Lib "oleaut32" (ByVal cDims As Long, ByRef psaInOut As Long)
    Public Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (a() As Any) As Long
    
    
    Public Sub MakeZeroToNegOneArray(pArray As Long, Optional cDims As Long = 1&)
        ' Works with all array types (numbers, UDTs, objects (early or late), fixed-length strings), except:
        '       BSTR (String) arrays.  Use s() = Split(vbNullString) instead.
        '       Probably shouldn't be used with arrays IN varants, but a Varant array should be just fine.
        '       Non-dynamic arrays.  This is obvious, but still important to remember.
        '
        ' WARNING:  Before calling this, make SURE the array is ERASED.
        ' WARNING:  You can't use REDIM PRESERVE on these things.  Just use REDIM TheArray(Low, High)
        '           We could also call SafeArrayAllocData which would make these things more versatile (allowing Redim Preserve).
        '
        ' Example:  Erase SomeArray
        '           MakeZeroToNegOneArray ArrPtr(SomeArray)
        '
        SafeArrayAllocDescriptor cDims, ByVal pArray
        'SafeArrayAllocData ByVal pArray                ' No reason to NOT do this, but we don't.  This would allow ReDim PRESERVE.
    End Sub
    
    
    
    Please read the comments, as they're important. This thing does have a couple of limits (BSTR strings and arrays within variants).

    Also, it assumes we're starting with an "Erased" or never dimensioned array.
    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. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  13. #13
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    7,930

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by wqweto View Post
    Edit: Ooops, there is a problem with using empty UDT array ...
    Seems to work ok for me. I just took my post #12 code and put it into a BAS module, then put the following into a Form1:

    Code:
    
    Option Explicit
    
    Private Type MyUdt
        i As Long
        s As String
    End Type
    
    Private Sub Form_Load()
        Dim u() As MyUdt
    
        MakeZeroToNegOneArray ArrPtr(u)
    
        Debug.Print LBound(u), UBound(u)
    
    
    End Sub
    
    
    Printed out 0 and -1.
    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. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  14. #14
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    4,048

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by Elroy View Post
    Seems to work ok for me. I just took my post #12 code and put it into a BAS module, then put the following into a Form1:

    Code:
    
    Option Explicit
    
    Private Type MyUdt
        i As Long
        s As String
    End Type
    
    Private Sub Form_Load()
        Dim u() As MyUdt
    
        MakeZeroToNegOneArray ArrPtr(u)
    
        Debug.Print LBound(u), UBound(u)
    
    
    End Sub
    
    
    Printed out 0 and -1.
    Try to follow this with

    ReDim Preserve u(0 To 10) As MyUdt
    u(0).i = 42


    Second line assignment fails with resized UDT arrays but works ok with arrays of scalar (primitive) types.

    cheers,
    </wqw>

  15. #15
    PowerPoster
    Join Date
    Jun 2013
    Posts
    6,122

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by wqweto View Post
    Try to follow this with

    ReDim Preserve u(0 To 10) As MyUdt
    u(0).i = 42


    Second line assignment fails with resized UDT arrays but works ok with arrays of scalar (primitive) types.
    Your "Redim PreserveAndSetValue-Test" fails also with primitive typed Arrays
    (have just corrected my code in #10 appropriately to):

    Code:
    Option Explicit
     
    Private Declare Sub Assign Lib "kernel32" Alias "RtlMoveMemory" (Dst As Any, Src As Any, Optional ByVal CB& = 4)
    Private Declare Function SafeArrayAllocDescriptor Lib "oleaut32" (ByVal Dims As Long, ByVal pSA As Long) As Long
     
    Private Sub Main()
        Dim Arr() As String
     
        InitEmptyArray Arr '<- works generically also for Object(), String(), Long() etc.
        Debug.Print LBound(Arr), UBound(Arr), UBound(Arr) - LBound(Arr) + 1, TypeName(Arr)
        
        ReDim Preserve Arr(0 To 3): Arr(0) = 123 '<- test, whether cbElements was set properly
        Debug.Print LBound(Arr), UBound(Arr), UBound(Arr) - LBound(Arr) + 1, Arr(0)
    End Sub
    
    Public Sub InitEmptyArray(Arr, Optional ByVal Dimensions& = 1)
      If IsArray(Arr) Then Erase Arr Else Exit Sub
      Dim pSA As Long: Assign pSA, ByVal VarPtr(Arr) + 8
      If pSA Then SafeArrayAllocDescriptor Dimensions, pSA Else Exit Sub
      
      Dim SA As Long: Assign SA, ByVal pSA: If SA = 0 Then Exit Sub
      Select Case VarType(Arr) And Not 8192
          Case vbByte:                       Assign ByVal SA + 4, 1&
          Case vbInteger, vbBoolean:         Assign ByVal SA + 4, 2&
          Case vbVariant, vbDecimal:         Assign ByVal SA + 4, 16&
          Case vbDouble, vbDate, vbCurrency: Assign ByVal SA + 4, 8&
          Case Else:                         Assign ByVal SA + 4, 4&
      End Select
    End Sub
    Olaf

  16. #16
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    7,731

    Re: Create an object array with a length of zero (or UBound = -1)

    Lots of impressive wizardry in this thread. Nonetheless, I'm going to take a shot in the dark here and assume that this is about your transpiler again SDO. If it is not about that feel free to ignore this post.

    Reading your OP, I got the impression you want to replicate the object oriented behavior of arrays seen in modern languages like C# or Python. What you must understand is that you cannot graft VB6's arrays 1:1 onto array types in modern high level languages. VB6 arrays are more closely related to C arrays than they are to C# arrays or Python arrays. Arrays are typically full blown objects in modern languages. They may have special compiler support in some cases like in C# to optimize their performance.

    Thankfully, as far as transpiling goes, this is one of the easier things to replicate in VB6. You can start by having your transpiler auto-generate a class like this for every array type used in your scripting language:-
    Code:
    '=====================
    NativeArray_String.cls
    '=====================
    
    Private g_arr() As String
    
    Private g_len As Long
    
    Public Property Get Length() As Long
        Length = g_len
    End Property
    
    Public Property Get Item(ByVal index As Long) As String
        Item = g_arr(index)
    End Property
    
    Public Property Let Item(ByVal index As Long, ByVal value As String)
        g_arr(index) = value
    End Property
    
    Public Sub SetSize(ByVal size As Long)
        
        If size = 0 Then
            Erase g_arr
        Else
            ReDim g_arr(0 To size - 1)
        End If
       
        g_len = size
    End Sub
    
    Public Sub SetData(ParamArray data() As Variant)
        Dim i As Long
        
        ReDim g_arr(0 To UBound(data))
        
        For i = 0 To UBound(data)
            g_arr(i) = data(i)
        Next
    
        g_len = UBound(data) + 1
    End Sub
    The above is one for a String array. Highlighted in red are points where the type of the array is represented so if it were an array of Longs, they would both be Long instead of String. Your transpiler should generate one of each type of array used in code.

    Now using VB.Net as a stand-in for your scripting language, the following could be transpiled to VB6:-
    Code:
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim data As String()
    
            'Test for null
            Debug.WriteLine(data Is Nothing)
    
            'Create array of size 10
            data = New String(9) {}
    
            Debug.WriteLine(data.Length)
    
            'Create empty array
            data = {}
    
            Debug.WriteLine(data.Length)
    
            'Assign an array with data
            data = {"Hello", "World"}
    
            For i = 0 To data.Length - 1
                Debug.WriteLine(data(i))
            Next
    
            'Change an element
            data(1) = "WORLD!!!!"
    
            Debug.WriteLine(data(1))
    
        End Sub
    As this:-
    Code:
    Private Sub Form_Load()
        Dim i As Long
        Dim data As NativeArray_String
        
        'Test for null
        Debug.Print data Is Nothing
        
        'Create array of size 10
        Set data = New NativeArray_String
        data.SetSize 10
        
        Debug.Print data.Length
        
        'Create empty array
        data.SetSize 0
        
        Debug.Print data.Length
        
        'Assign an array with data
        data.SetData "Hello", "world"
        
        For i = 0 To data.Length - 1
            Debug.Print data.Item(i)
        Next
        
       'Change an element
       data.Item(1) = "WORLD!!!"
       
       Debug.Print data.Item(1)
    End Sub
    This way you get full object oriented behavior from arrays successfully represented in VB6 including the ability to pass the reference by value, straightforward nullability and zero length arrays.
    Last edited by Niya; Jun 28th, 2022 at 04:51 PM.
    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

  17. #17
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    7,930

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by wqweto View Post
    Try to follow this with

    ReDim Preserve u(0 To 10) As MyUdt
    u(0).i = 42


    Second line assignment fails with resized UDT arrays but works ok with arrays of scalar (primitive) types.

    cheers,
    </wqw>
    Yep, you're correct. But, why are we trying to preserve a 0 to -1 array. I agree that it's a "got'cha", but, if we're careful it'll work just fine.
    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. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  18. #18
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    4,048

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by Elroy View Post
    But, why are we trying to preserve a 0 to -1 array.
    Usually it's when you append to an dynamic array you just check size and resize if needed and then assign last element If lSize > UBound(arr) Then ReDim Preserve arr(0 To lSize * 2) End If : arr(lSize) = NewEntry or similar.

    cheers,
    </wqw>

  19. #19

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by Schmidt View Post
    In case this request is only, because you want the initialization to UBound=-1 more generically
    (working with any simple type as Object(), SomeClass(), String(), Long(), etc.) -
    then the following might be of use:

    Code:
    Option Explicit
     
    Private Declare Sub Assign Lib "kernel32" Alias "RtlMoveMemory" (Dst As Any, Src As Any, Optional ByVal CB& = 4)
    Private Declare Function SafeArrayAllocDescriptor Lib "oleaut32" (ByVal Dims As Long, ByVal pSA As Long) As Long
     
    Private Sub Main()
        Dim Arr() As String
     
        InitEmptyArray Arr '<- works generically also for Object(), String(), Long() etc.
        Debug.Print LBound(Arr), UBound(Arr), UBound(Arr) - LBound(Arr) + 1, TypeName(Arr)
        
        ReDim Preserve Arr(0 To 3): Arr(0) = 123 '<- test, whether cbElements was set properly
        Debug.Print LBound(Arr), UBound(Arr), UBound(Arr) - LBound(Arr) + 1, Arr(0)
    End Sub
    
    Public Sub InitEmptyArray(Arr, Optional ByVal Dimensions& = 1)
      If IsArray(Arr) Then Erase Arr Else Exit Sub
      Dim pSA As Long: Assign pSA, ByVal VarPtr(Arr) + 8
      If pSA Then SafeArrayAllocDescriptor Dimensions, pSA Else Exit Sub
      
      Dim SA As Long: Assign SA, ByVal pSA: If SA = 0 Then Exit Sub
      Select Case VarType(Arr) And Not 8192
          Case vbByte:                       Assign ByVal SA + 4, 1&
          Case vbInteger, vbBoolean:         Assign ByVal SA + 4, 2&
          Case vbVariant, vbDecimal:         Assign ByVal SA + 4, 16&
          Case vbDouble, vbDate, vbCurrency: Assign ByVal SA + 4, 8&
          Case Else:                         Assign ByVal SA + 4, 4&
      End Select
    End Sub
    HTH

    Olaf
    Very useful, thank you Olaf.

  20. #20

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by wqweto View Post
    Tweak the SafeArrayAllocDescriptor API declare and can be used without wrapping the SA pointer into a Variant like this

    Code:
    Option Explicit
    
    Private Declare Function SafeArrayAllocDescriptor Lib "oleaut32" (ByVal Dims As Long, pSA() As Any) As Long
    
    Private Type MyUdt
        ID As Long
    End Type
    
    Private Sub Form_Load()
        Dim arrObject()     As Object
        Dim arrUdt()        As MyUdt
        
        Call SafeArrayAllocDescriptor(1, arrObject)
        Debug.Print UBound(arrObject)
        
        '--- works with UDTs too
        Call SafeArrayAllocDescriptor(1, arrUdt)
        Debug.Print UBound(arrUdt)
    End Sub
    Edit: Ooops, there is a problem with using empty UDT array like the one above that we have already discussed here where there are some more ideas too.

    cheers,
    </wqw>
    Very useful code and info, thank you, wqweto.

  21. #21

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by Elroy View Post
    I do this all the time. In fact, I've gotten to where I virtually never return undimensioned arrays from functions that return arrays. I'd much rather have one of these 0 to -1 arrays so I can put it straight into a loop (that wouldn't execute).

    Code:
    
    Option Explicit
    
    
    Private Declare Sub SafeArrayAllocDescriptor Lib "oleaut32" (ByVal cDims As Long, ByRef psaInOut As Long)
    Public Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (a() As Any) As Long
    
    
    Public Sub MakeZeroToNegOneArray(pArray As Long, Optional cDims As Long = 1&)
        ' Works with all array types (numbers, UDTs, objects (early or late), fixed-length strings), except:
        '       BSTR (String) arrays.  Use s() = Split(vbNullString) instead.
        '       Probably shouldn't be used with arrays IN varants, but a Varant array should be just fine.
        '       Non-dynamic arrays.  This is obvious, but still important to remember.
        '
        ' WARNING:  Before calling this, make SURE the array is ERASED.
        ' WARNING:  You can't use REDIM PRESERVE on these things.  Just use REDIM TheArray(Low, High)
        '           We could also call SafeArrayAllocData which would make these things more versatile (allowing Redim Preserve).
        '
        ' Example:  Erase SomeArray
        '           MakeZeroToNegOneArray ArrPtr(SomeArray)
        '
        SafeArrayAllocDescriptor cDims, ByVal pArray
        'SafeArrayAllocData ByVal pArray                ' No reason to NOT do this, but we don't.  This would allow ReDim PRESERVE.
    End Sub
    
    
    
    Please read the comments, as they're important. This thing does have a couple of limits (BSTR strings and arrays within variants).

    Also, it assumes we're starting with an "Erased" or never dimensioned array.
    Very useful code, thank you, Elroy.

  22. #22

    Thread Starter
    Fanatic Member
    Join Date
    Aug 2020
    Posts
    845

    Re: Create an object array with a length of zero (or UBound = -1)

    Quote Originally Posted by Niya View Post
    Lots of impressive wizardry in this thread. Nonetheless, I'm going to take a shot in the dark here and assume that this is about your transpiler again SDO. If it is not about that feel free to ignore this post.

    Reading your OP, I got the impression you want to replicate the object oriented behavior of arrays seen in modern languages like C# or Python. What you must understand is that you cannot graft VB6's arrays 1:1 onto array types in modern high level languages. VB6 arrays are more closely related to C arrays than they are to C# arrays or Python arrays. Arrays are typically full blown objects in modern languages. They may have special compiler support in some cases like in C# to optimize their performance.

    Thankfully, as far as transpiling goes, this is one of the easier things to replicate in VB6. You can start by having your transpiler auto-generate a class like this for every array type used in your scripting language:-
    Code:
    '=====================
    NativeArray_String.cls
    '=====================
    
    Private g_arr() As String
    
    Private g_len As Long
    
    Public Property Get Length() As Long
        Length = g_len
    End Property
    
    Public Property Get Item(ByVal index As Long) As String
        Item = g_arr(index)
    End Property
    
    Public Property Let Item(ByVal index As Long, ByVal value As String)
        g_arr(index) = value
    End Property
    
    Public Sub SetSize(ByVal size As Long)
        
        If size = 0 Then
            Erase g_arr
        Else
            ReDim g_arr(0 To size - 1)
        End If
       
        g_len = size
    End Sub
    
    Public Sub SetData(ParamArray data() As Variant)
        Dim i As Long
        
        ReDim g_arr(0 To UBound(data))
        
        For i = 0 To UBound(data)
            g_arr(i) = data(i)
        Next
    
        g_len = UBound(data) + 1
    End Sub
    The above is one for a String array. Highlighted in red are points where the type of the array is represented so if it were an array of Longs, they would both be Long instead of String. Your transpiler should generate one of each type of array used in code.

    Now using VB.Net as a stand-in for your scripting language, the following could be transpiled to VB6:-
    Code:
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim data As String()
    
            'Test for null
            Debug.WriteLine(data Is Nothing)
    
            'Create array of size 10
            data = New String(9) {}
    
            Debug.WriteLine(data.Length)
    
            'Create empty array
            data = {}
    
            Debug.WriteLine(data.Length)
    
            'Assign an array with data
            data = {"Hello", "World"}
    
            For i = 0 To data.Length - 1
                Debug.WriteLine(data(i))
            Next
    
            'Change an element
            data(1) = "WORLD!!!!"
    
            Debug.WriteLine(data(1))
    
        End Sub
    As this:-
    Code:
    Private Sub Form_Load()
        Dim i As Long
        Dim data As NativeArray_String
        
        'Test for null
        Debug.Print data Is Nothing
        
        'Create array of size 10
        Set data = New NativeArray_String
        data.SetSize 10
        
        Debug.Print data.Length
        
        'Create empty array
        data.SetSize 0
        
        Debug.Print data.Length
        
        'Assign an array with data
        data.SetData "Hello", "world"
        
        For i = 0 To data.Length - 1
            Debug.Print data.Item(i)
        Next
        
       'Change an element
       data.Item(1) = "WORLD!!!"
       
       Debug.Print data.Item(1)
    End Sub
    This way you get full object oriented behavior from arrays successfully represented in VB6 including the ability to pass the reference by value, straightforward nullability and zero length arrays.
    You've always inspired me a lot when it comes to transpilers. Thank you, Niya.

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