Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Arrays as UDTs
#1
@Unseen Machine

So I'm taking a look at John's demo here: https://qb64phoenix.com/forum/showthread...7#pid38857

It took me back to my Atari Basic days. Atari had well supported peek/poke documentation. Couldn't do exactly what you wanted with the keywords? Poke it! So it seems John took a similar approach to pass arrays as _Offset.

It's neat that it didn't require a lot of new stuff under the hood to accomplish this, a little over 3K or is that UK where John hails form? 

I want to take his example and try it out on something SCREEN 0 simple. I think I'm suffering from heat exhaustion today. Played some golf, but should have gone to the beach, instead. Oh well, beats ice fishing.

Anyway, I'll reply back in this thread if I have any questions for John, or form any helpful insights for others. Until then, I suggest we all jut use COMMON SHARED for all variables... mostly because that really pisses off Bill.

Pete
Reply
#2
What I gleaned from Unseen... @Unseen Machine

Code: (Select All)
'$Include:'qb_universal.bi'
Dim MemSystem As _Offset
MemSystem = Var_New ' Library function.
text$(1) = "Q: What's the difference between a monkey and Steve?"
text$(2) = "A: A monkey eats the banana and the peel. Steve just eats the peel."
Dim m As _Offset
m = Var_New: Var_SetString m, text$(1): Var_Push MemSystem, m
m = Var_New: Var_SetString m, text$(2): Var_Push MemSystem, m
pete MemSystem
Sleep
Var_Free MemSystem
End

Sub pete (MemSystem As _Offset)
    For i = 0 To 1
        Dim node As _Offset: node = Var_At(MemSystem, i)
        t$ = Var_GetString$(node)
        Print t$
    Next
End Sub

So by checking the output we can tell your system is totally valid!

Pete


.h   QB_UNIVERSAL.h (Size: 2.09 KB / Downloads: 4)

.bi   QB_Universal.bi (Size: 799 bytes / Downloads: 2)
Reply
#3
Code: (Select All)
'$Include:'qb_universal.bi'
Dim MemSystem As _Offset
MemSystem = Var_New ' Library function.
Dim p As _Offset, c As _Offset
Dim fruit$(4), meat$(3)
fruit$(1) = "apple": fruit$(2) = "orange": fruit$(3) = "pear": fruit$(4) = "banana": meat$(1) = "steak": meat$(2) = "chicken": meat$(3) = "bacon"
p = Var_New: Var_SetString p, "Fruits": Var_Push MemSystem, p
For i = 1 To UBound(fruit$): c = Var_New: Var_SetString c, fruit$(i): Var_Push p, c: Next
p = Var_New: Var_SetString p, "Meats": Var_Push MemSystem, p
For i = 1 To UBound(meat$): c = Var_New: Var_SetString c, meat$(i): Var_Push p, c: Next
pete MemSystem
Sleep
Var_Free MemSystem
End

Sub pete (MemSystem As _Offset)
    Dim node As _Offset, p As _Offset
    For i = 0 To Var_Count(MemSystem) - 1
        node = Var_At(MemSystem, i)
        t$ = Var_GetString$(node)
        Print "Parent> "; t$
        p = Var_At(MemSystem, i)
        For j = 0 To Var_Count(p) - 1
            t$ = Var_GetString$(Var_At(p, j))
            Print "Child>>> "; t$
        Next
    Next
End Sub

So @Unseen Machine am I getting the use of nodes correct here?

Pete

Edit: Well this part didn't work out. I put the parent array in a function and the output superimposes the meat over the fruit! I used "Fruit123" to make it easier to see. So the parent array "Meat" is displayed as: Meatt123

Code: (Select All)
'$Include:'qb_universal.bi'
Dim MemSystem As _Offset
MemSystem = Var_New ' Library function.
Dim p As _Offset
Dim fruit$(4), meat$(3)
fruit$(1) = "apple": fruit$(2) = "orange": fruit$(3) = "pear": fruit$(4) = "banana": meat$(1) = "steak": meat$(2) = "chicken": meat$(3) = "bacon"
p = Parent_Node("Fruit123", MemSystem): Child_Node fruit$(), p
p = Parent_Node("Meat", MemSystem): Child_Node meat$(), p
pete MemSystem
Sleep
Var_Free MemSystem
End

Function Parent_Node%& (Parent As String, MemSystem As _Offset)
Dim a As _Offset
a = Var_New: Var_SetString a, Parent: Var_Push MemSystem, a: Parent_Node = a
End Function

Sub Child_Node (array$(), p As _Offset)
Dim c As _Offset
For i = 1 To UBound(array$)
c = Var_New: Var_SetString c, array$(i): Var_Push p, c
Next
End Sub

Sub pete (MemSystem As _Offset)
Dim node As _Offset, p As _Offset
For i = 0 To Var_Count(MemSystem) - 1
node = Var_At(MemSystem, i)
t$ = Var_GetString$(node)
Print "Parent> "; t$
p = Var_At(MemSystem, i)
For j = 0 To Var_Count(p) - 1
t$ = Var_GetString$(Var_At(p, j))
Print "Child>>> "; t$
Next
Next
End Sub

Now if I take the function and move it into the main, it works...

Code: (Select All)
'$Include:'qb_universal.bi'
Dim MemSystem As _Offset
MemSystem = Var_New ' Library function.
Dim p As _Offset
Dim fruit$(4), meat$(3)
fruit$(1) = "apple": fruit$(2) = "orange": fruit$(3) = "pear": fruit$(4) = "banana": meat$(1) = "steak": meat$(2) = "chicken": meat$(3) = "bacon"
Dim a As _Offset
a = Var_New: Var_SetString a, "Fruit123": Var_Push MemSystem, a: Parent_Node = a: p = a: Child_Node fruit$(), p
a = Var_New: Var_SetString a, "Meat": Var_Push MemSystem, a: Parent_Node = a: p = a: Child_Node meat$(), p
pete MemSystem
Sleep
Var_Free MemSystem
End

Sub Child_Node (array$(), p As _Offset)
    Dim c As _Offset
    For i = 1 To UBound(array$)
        c = Var_New: Var_SetString c, array$(i): Var_Push p, c
    Next
End Sub

Sub pete (MemSystem As _Offset)
    Dim node As _Offset, p As _Offset
    For i = 0 To Var_Count(MemSystem) - 1
        node = Var_At(MemSystem, i)
        t$ = Var_GetString$(node)
        Print "Parent> "; t$
        p = Var_At(MemSystem, i)
        For j = 0 To Var_Count(p) - 1
            t$ = Var_GetString$(Var_At(p, j))
            Print "Child>>> "; t$
        Next
    Next
End Sub

So is something buggy or did I miss something in the function?
Reply
#4
If these are calling out to C-style functions, they may need to be CHR$(0) null terminated.
Reply
#5
(02-03-2026, 02:28 AM)SMcNeill Wrote: If these are calling out to C-style functions, they may need to be CHR$(0) null terminated.
@SMcNeill Thats probably it, ill have a look for you @Pete tomorrow mate! Thanks for giving it a blast though! Makes me happy to have finally made something useful!

Unseen
Reply
#6
"Arrays as UDTs," what do you mean by that? Do you mean record arrays?
Reply
#7
STEVE REALLY IS AMAZING! Even if he only eats the peel. Big Grin

So I added Parent = Parent + Chr$(0) to the function, and it works as intended. No more fruit in Pete's meat. Ah, forget that last part.

I still think some things need to be investigated. For starters, we don't have to add a terminal chr$(0) to the child process, only the parent one. Next, I still don't understand that the chr$(0) is not needed if the Var_ functions are moved out of the QB4 Parent_Node%& function, and into the main. That behavior is the type of inconsistency certain to create problems for other users. (Except for non-functional users). Big Grin

To @Kernalpanic this started out as a way to get arrays in user define types. This is close. We still cannot call our array mytype.array() and pop it in the type definition with the rest of the mytype variables, but we can pass all of our arrays as a single array() name in our subs or functions. So no more: Sub ThisSub (mytype as udts, FruitArray$(), MeatArray$(), VeggieArray$(), DairyArray$()). Now we just use:  Sub ThisSub (mytype as udts, MemSystem As _Offset).

Now for practical use, I'll have to weigh the benefit above with the fact we have to unpack that memsystem to use those arrays. That will be somewhat involved, but until I try it out on some complex stuff, I won't know just how involved. One concern I see is will I have to unpack it for every sub call in a program that would otherwise share arrays in most of the subroutines.

Anyway, thanks to Steve, here is the working code without the parent string overlap problem...
Code: (Select All)
'$Include:'qb_universal.bi'
Dim MemSystem As _Offset
MemSystem = Var_New ' Library function.
Dim p As _Offset
Dim fruit$(4), meat$(3)
fruit$(1) = "apple": fruit$(2) = "orange": fruit$(3) = "pear": fruit$(4) = "banana": meat$(1) = "steak": meat$(2) = "chicken": meat$(3) = "bacon"
p = Parent_Node("Fruit123", MemSystem): Child_Node fruit$(), p
p = Parent_Node("Meat", MemSystem): Child_Node meat$(), p
pete MemSystem
Var_Free MemSystem
End

Function Parent_Node%& (Parent As String, MemSystem As _Offset)
    Dim a As _Offset
    Parent = Parent + Chr$(0) ' C null terminator. <----- Required when a 'Parent' string is sent to a sub or function.
    a = Var_New: Var_SetString a, Parent: Var_Push MemSystem, a: Parent_Node = a
End Function

Sub Child_Node (array$(), p As _Offset)
    Dim c As _Offset
    For i = 1 To UBound(array$)
        c = Var_New: Var_SetString c, array$(i): Var_Push p, c
    Next
End Sub

Sub pete (MemSystem As _Offset)
    Dim node As _Offset, p As _Offset
    For i = 0 To Var_Count(MemSystem) - 1
        node = Var_At(MemSystem, i)
        t$ = Var_GetString$(node)
        Print "Parent> "; t$; "{" ' See, the chr$(0) is automatically chopped off.
        p = Var_At(MemSystem, i)
        For j = 0 To Var_Count(p) - 1
            t$ = Var_GetString$(Var_At(p, j))
            Print "Child>>> "; t$; "{"
        Next
    Next
End Sub

Pete
Reply
#8
Ideally, something like this would track string and size both on the back side of the c header.

A format such as:   Length of string stored as LONG, then the string stored.   Then you just read the first four bytes, get the length, and then know exactly how many bytes to get for the string itself.

The issue is QB64 does NOT null terminate strings.   "HELLO" + CHR$(0) + "WORLD" is a perfectly valid QB64 string.  In C, that would read as *just* "HELLO" + terminator.   The way to bypass that termination character is to count bytes and say, "OH I NEEDS 11 BYTES TO RETURN!!"

Unseen needs to change his structure to store length + string, and not just string alone, so it'd be usable in QB64 without issues.
Reply
#9
Code: (Select All)
    __declspec(dllexport) int QB_GetStringLength(QBHandle h) {
        return h ? (int)((QBNode*)h)->s.size() : 0;
    }
Add that to the .h file (just above the last } at the bottom 
Code: (Select All)
  FUNCTION Var_GetStringLength& ALIAS QB_GetStringLength (BYVAL Str AS _OFFSET)

That to the .bi declares block 

And demo usage :
Code: (Select All)

'$DYNAMIC
'$INCLUDE:'qb_universal.bi'

SCREEN _NEWIMAGE(800, 600, 32)

' --- DATA INITIALIZATION ---
DIM SHARED MenuSystem AS _OFFSET
MenuSystem = Var_New

' Build Edit Menu
DIM EditMenu AS _OFFSET: EditMenu = BuildMenu("&Edit")
AddWordItem EditMenu, "A"
AddWordItem EditMenu, "ABC"
AddWordItem EditMenu, "ABCDE"
AddWordItem EditMenu, "ABCDEFGH"
AddWordItem EditMenu, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

FOR i% = 0 TO 4
  PRINT Var_GetStringLength(Var_At(EditMenu, i%))
NEXT
SLEEP
Var_Free EditMenu
SYSTEM

' --- SUBS AND FUNCTIONS ---

FUNCTION BuildMenu%& (Title AS STRING)
  DIM m AS _OFFSET: m = Var_New
  Var_SetString m, Title
  Var_Push MenuSystem, m
  BuildMenu = m
END FUNCTION

SUB AddWordItem (parent AS _OFFSET, text AS STRING)
  DIM n AS _OFFSET: n = Var_New
  Var_SetString n, text
  Var_Push parent, n
END SUB
Reply
#10
(02-03-2026, 06:09 PM)SMcNeill Wrote: Ideally, something like this would track string and size both on the back side of the c header.

A format such as:   Length of string stored as LONG, then the string stored.   Then you just read the first four bytes, get the length, and then know exactly how many bytes to get for the string itself.

The issue is QB64 does NOT null terminate strings.   "HELLO" + CHR$(0) + "WORLD" is a perfectly valid QB64 string.  In C, that would read as *just* "HELLO" + terminator.   The way to bypass that termination character is to count bytes and say, "OH I NEEDS 11 BYTES TO RETURN!!"

Unseen needs to change his structure to store length + string, and not just string alone, so it'd be usable in QB64 without issues.

Speaking of tracking string and length...

Pure QB64 method to pass arrays.
Code: (Select All)
Type foo
    index As String
    array As String
    build As String
    id As Integer
End Type
Dim z As foo

Concat_Arrays z: Display_Arrays z

Sub Concat_Arrays (z As foo)
    While -1
        i = i + 1: j = 0
        Do
            Read a$
            If a$ = "eof" Then Exit While
            If a$ = "eol" Then Exit Do
            z.index = z.index + LTrim$(Str$(i)) + "," + LTrim$(Str$(Len(a$))) + "|" ' Parent and length of string added.
            z.array = z.array + a$
            j = j + 1
        Loop
    Wend
End Sub

Sub Display_Arrays (z As foo)
    ReDim a(10), Fruits$(10), Veggies$(10), Meats$(10)
    Do
        Unpack z: If z.id = 0 Then Exit Do
        If z.id <> oldid Then a = 0
        a = a + 1
        Select Case z.id
            Case 1: Fruits$(a) = z.build
            Case 2: Veggies$(a) = z.build
            Case 3: Meats$(a) = z.build
        End Select
        oldid = z.id
    Loop
    For i = 1 To UBound(Fruits$)
        If Len(Fruits$(i)) Then Print Fruits$(i)
    Next
    Print
    For i = 1 To UBound(Veggies$)
        If Len(Veggies$(i)) Then Print Veggies$(i)
    Next
    Print
    For i = 1 To UBound(Meats$)
        If Len(Meats$(i)) Then Print Meats$(i)
    Next
End Sub

Sub Unpack (z As foo)
    Static seed, c
    j = InStr(seed, z.index, ","): If j = 0 Then z.id = 0: Exit Sub
    z.id = Val(Mid$(z.index, j - 1))
    b = Val(Mid$(z.index, j + 1, InStr(Mid$(z.index, j + 1), "|")))
    seed = j + 1
    z.build = Mid$(z.array, c + 1, b)
    c = c + b
End Sub

Data Fruits,Apple,Orange,Pear,Banana,Plum,eol
Data Veggies,Squash,Peas,Green Beans,Carrot,Celery,eol
Data Meats,Steak,Bacon,Chicken,Fish,eol
Data eof

We have other examples in a thread from September: https://qb64phoenix.com/forum/showthread.php?tid=3956

Well, as fun as this is, and I have to go try Unseen's header fixes, I cant see myself duplicating the routine, that gathers the arrays from memory, in every sub they will be needed in. One or two, fine, several, I can't see myself doing that, because it no longer would be a time saver.

Anyway, this reminds me of the start of that song from the time Jesus was coding as a baby. You know... "Array in a manger..." Big Grin

Pete

- The 10 Commandments. It's not like its written in stone... Oh wait!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Arrays inside Types? Ikerkaz 7 169 Yesterday, 04:21 PM
Last Post: ahenry3068
  Preserving multi-dim arrays Pete 5 390 12-19-2025, 03:17 PM
Last Post: Dimster
  Array out of passing arrays... Pete 2 393 09-22-2025, 08:53 PM
Last Post: ahenry3068
  Arrays of record variables Kernelpanic 0 447 04-02-2024, 06:58 PM
Last Post: Kernelpanic

Forum Jump:


Users browsing this thread: 1 Guest(s)