QB64 Phoenix Edition
Arrays In User Data Types - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: QB64 Rising (https://qb64phoenix.com/forum/forumdisplay.php?fid=1)
+--- Forum: Code and Stuff (https://qb64phoenix.com/forum/forumdisplay.php?fid=3)
+---- Forum: Help Me! (https://qb64phoenix.com/forum/forumdisplay.php?fid=10)
+---- Thread: Arrays In User Data Types (/showthread.php?tid=2392)



Arrays In User Data Types - Consolemu - 01-18-2024

Hello everyone,

I'm not sure if this question has been asked before and if so my apologies.  I tried searching online for the topic but those solutions weren't necessarily applicable to what I'm looking for.  I'm working on a program for practice where I take a text file, read and parse the data into an array, then save the array to my own custom binary file.  This task is tricky in most languages but a breeze in C as far as creating a struct that contains an array that can be written to or read from a binary file.  But with QB when I try this code for example I get an error:

Type MyPalette
HeaderTag as String
    Colors(20) As integer

End Type

For as long as I've been coding off and on in Basic and QB especially I didn't know you couldn't simply create a fixed array in a custom data.  With binary files I'll typically create a data type with a header and variables where each variable in succession including large chunks of data can be organized into a contained array so I can write the entire custom data type to a binary file with one line of code.  Is this even possible with QB or is there any type of workaround?  I did glance at the new _MEM command but not too familiar with that either.

Thanks for reading and your input.


RE: Arrays In User Data Types - justsomeguy - 01-18-2024

Sorry to say, to my knowledge QB64 does not directly support that. However I have had a similar issue and have a couple of work arounds.

Two methods I have are to use STRINGS or to use _MEM's. They both have their pro and cons.

String method is the simpliest.
Code: (Select All)

' String Array Test
' Arrays must start at index 1

TYPE tUDT
  longArray AS STRING * 1028 ' arraysize * 4 + 4
END TYPE

DIM udt AS tUDT
DIM iter AS LONG

FOR iter = 1 TO 256
  SetArrayLong udt.longArray, iter, iter * 2000000
  PRINT iter; ": "; getArrayLong(udt.longArray, iter)
NEXT

FUNCTION getArrayLong& (s AS STRING, p AS LONG)
  IF p > 0 AND p * 4 + 4 <= LEN(s) THEN getArrayLong = CVL(MID$(s, p * 4, 4))
END FUNCTION

SUB SetArrayLong (s AS STRING, p AS LONG, v AS LONG)
  IF p > 0 AND p * 4 + 4 <= LEN(s) THEN MID$(s, p * 4) = MKL$(v)
END SUB

FUNCTION getArraySingle! (s AS STRING, p AS LONG)
  IF p > 0 AND p * 4 + 4 <= LEN(s) THEN getArraySingle = CVS(MID$(s, p * 4, 4))
END FUNCTION

SUB SetArraySingle (s AS STRING, p AS LONG, v AS SINGLE)
  IF p > 0 AND p * 4 + 4 <= LEN(s) THEN MID$(s, p * 4) = MKS$(v)
END SUB

FUNCTION getArrayInteger% (s AS STRING, p AS LONG)
  IF p > 0 AND p * 2 + 2 <= LEN(s) THEN getArrayInteger = CVI(MID$(s, p * 2, 2))
END FUNCTION

SUB SetArrayInteger (s AS STRING, p AS LONG, v AS INTEGER)
  IF p > 0 AND p * 2 + 2 <= LEN(s) THEN MID$(s, p * 2) = MKI$(v)
END SUB

FUNCTION getArrayDouble# (s AS STRING, p AS LONG)
  IF p > 0 AND p * 8 + 8 <= LEN(s) THEN getArrayDouble = CVL(MID$(s, p * 8, 8))
END FUNCTION

SUB SetArrayDouble (s AS STRING, p AS LONG, v AS DOUBLE)
  IF p > 0 AND p * 8 + 8 <= LEN(s) THEN MID$(s, p * 8) = MKD$(v)
END SUB

The demo above uses longs which is 4 bytes. Integers in QB64 are 2 bytes.

The _MEM method is a bit more complicated.

Code: (Select All)
' Program to create an arrays in a UDT
SCREEN 12: _FONT 8
_TITLE "UDT_ARRAYS.bas"
DIM AS LONG i
DIM AS DOUBLE v
CONST cMAXELEMENTS = 10

' set byte counts for the data types
CONST cDT_BYTE = 1
CONST cDT_INTEGER = 2
CONST cDT_LONG = 4
CONST cDT_SINGLE = 4
CONST cDT_DOUBLE = 8
CONST cDT_INTEGER64 = 8
CONST cDT_FLOAT = 32

TYPE tARRAYS
  UDT_Array0 AS _MEM ' Array of Doubles
  UDT_Array1 AS _MEM ' Array of Singles
  UDT_Array2 AS _MEM ' Array of Integers
END TYPE

DIM AS tARRAYS UDT

' initialize the array
createUDTArray UDT.UDT_Array0, cDT_DOUBLE, cMAXELEMENTS
createUDTArray UDT.UDT_Array1, cDT_SINGLE, cMAXELEMENTS
createUDTArray UDT.UDT_Array2, cDT_INTEGER, cMAXELEMENTS
PRINT "ARRAY Doubles      ARRAY Singles        ARRAY Integers"
' set the array to random values
PRINT "Create initial Values, Store them, and retrieve"
FOR i = 0 TO cMAXELEMENTS
  v = INT(RND * 5 * 100) / 100
  setElementDBL UDT.UDT_Array0, i, v
  PRINT USING "index: ##-> #.##    "; i; getElementDBL(UDT.UDT_Array0, i);
  v = INT(RND * 5 * 100) / 100
  setElementSIGL UDT.UDT_Array1, i, v
  PRINT USING "index: ##-> #.##    "; i; getElementSIGL(UDT.UDT_Array1, i);
  v = INT(RND * 5)
  setElementINT UDT.UDT_Array2, i, v
  PRINT USING "index: ##-> #.##"; i; getElementINT(UDT.UDT_Array2, i)
NEXT

'resize array
resizeUDTArray UDT.UDT_Array0, cDT_DOUBLE, cMAXELEMENTS * 2
resizeUDTArray UDT.UDT_Array1, cDT_SINGLE, cMAXELEMENTS * 2
resizeUDTArray UDT.UDT_Array2, cDT_INTEGER, cMAXELEMENTS * 2

' retrieve values from array
PRINT "Double the size of the array"
FOR i = 0 TO cMAXELEMENTS * 2
  PRINT USING "index: ##-> #.##    "; i; getElementDBL(UDT.UDT_Array0, i);
  PRINT USING "index: ##-> #.##    "; i; getElementSIGL(UDT.UDT_Array1, i);
  PRINT USING "index: ##-> #.##"; i; getElementINT(UDT.UDT_Array2, i)
NEXT

'resize array
resizeUDTArray UDT.UDT_Array0, cDT_DOUBLE, cMAXELEMENTS / 2
resizeUDTArray UDT.UDT_Array1, cDT_SINGLE, cMAXELEMENTS / 2
resizeUDTArray UDT.UDT_Array2, cDT_INTEGER, cMAXELEMENTS / 2

' retrieve values from array
PRINT "Half the size of the array"
FOR i = 0 TO cMAXELEMENTS / 2
  PRINT USING "index: ##-> #.##    "; i; getElementDBL(UDT.UDT_Array0, i);
  PRINT USING "index: ##-> #.##    "; i; getElementSIGL(UDT.UDT_Array1, i);
  PRINT USING "index: ##-> #.##"; i; getElementINT(UDT.UDT_Array2, i)
NEXT


SUB createUDTArray (o AS _MEM, dt AS LONG, size AS LONG)
  ' make array one larger than number of elements
  o = _MEMNEW((size + 1) * dt) ' 'dt' is the number of bytes in the datatype
END SUB

SUB resizeUDTArray (o AS _MEM, dt AS LONG, size AS LONG)
  DIM AS _MEM old
  DIM AS LONG iter
  ' Copy old _MEM to a temp _MEM
  old = o
  ' Create new array of the new size
  createUDTArray o, dt, size
  'clear new array
  iter = 0: DO WHILE iter < o.SIZE
    _MEMPUT o, o.OFFSET + iter, 0 AS _BYTE
  iter = iter + 1: LOOP
  ' Copy data from old array to new array
  iter = 0: DO WHILE iter < o.SIZE AND iter < old.SIZE
    _MEMPUT o, o.OFFSET + iter, _MEMGET(old, old.OFFSET + iter, _BYTE) AS _BYTE
  iter = iter + 1: LOOP
  ' free old array
  _MEMFREE old
END SUB

FUNCTION getElementBYTE%% (o AS _MEM, element AS LONG)
  getElementBYTE = _MEMGET(o, o.OFFSET + (element * cDT_BYTE), _BYTE)
END FUNCTION

SUB setElementBYTE (o AS _MEM, element AS LONG, v AS _BYTE)
  _MEMPUT o, o.OFFSET + (element * cDT_BYTE), v AS _BYTE
END SUB

FUNCTION getElementINT% (o AS _MEM, element AS LONG)
  getElementINT = _MEMGET(o, o.OFFSET + (element * cDT_INTEGER), INTEGER)
END FUNCTION

SUB setElementINT (o AS _MEM, element AS LONG, v AS INTEGER)
  _MEMPUT o, o.OFFSET + (element * cDT_INTEGER), v AS INTEGER
END SUB

FUNCTION getElementLNG& (o AS _MEM, element AS LONG)
  getElementLNG = _MEMGET(o, o.OFFSET + (element * cDT_LONG), LONG)
END FUNCTION

SUB setElementLNG (o AS _MEM, element AS LONG, v AS LONG)
  _MEMPUT o, o.OFFSET + (element * cDT_LONG), v AS LONG
END SUB

FUNCTION getElementSIGL! (o AS _MEM, element AS LONG)
  getElementSIGL = _MEMGET(o, o.OFFSET + (element * cDT_SINGLE), SINGLE)
END FUNCTION

SUB setElementSIGL (o AS _MEM, element AS LONG, v AS SINGLE)
  _MEMPUT o, o.OFFSET + (element * cDT_SINGLE), v AS SINGLE
END SUB

FUNCTION getElementDBL# (o AS _MEM, element AS LONG)
  getElementDBL = _MEMGET(o, o.OFFSET + (element * cDT_DOUBLE), DOUBLE)
END FUNCTION

SUB setElementDBL (o AS _MEM, element AS LONG, v AS DOUBLE)
  _MEMPUT o, o.OFFSET + (element * cDT_DOUBLE), v AS DOUBLE
END SUB

FUNCTION getElementINT64&& (o AS _MEM, element AS LONG)
  getElementINT64 = _MEMGET(o, o.OFFSET + (element * cDT_INTEGER64), _INTEGER64)
END FUNCTION

SUB setElementINT64 (o AS _MEM, element AS LONG, v AS _INTEGER64)
  _MEMPUT o, o.OFFSET + (element * cDT_INTEGER64), v AS _INTEGER64
END SUB

FUNCTION getElementFLT## (o AS _MEM, element AS LONG)
  getElementFLT = _MEMGET(o, o.OFFSET + (element * cDT_FLOAT), _FLOAT)
END FUNCTION

SUB setElementFLT (o AS _MEM, element AS LONG, v AS _FLOAT)
  _MEMPUT o, o.OFFSET + (element * cDT_FLOAT), v AS _FLOAT
END SUB


_MEM have to be initialized before use, but they can be resized.


RE: Arrays In User Data Types - bplus - 01-18-2024

To add to what justsomeguy said here is a demo for using a variable length string to hold an array of Long Values:
Code: (Select All)
Option _Explicit
' I modified this for LONG type only do one one for floats DOUBLE 2021-01-31  stored in handy
DefLng A-Z
ReDim i
Type t
    s As String
End Type
Dim v As t

For i = 10 To 0 Step -1
    setLong v.s, i, i ^ 2
Next i

For i = 0 To 10
    Print getLong&(v.s, i)
Next i

' just for kicks lets jump to 100
setLong v.s, 100, 100 ^ 2

' now check the index 90 to 100
For i = 90 To 100
    Print getLong&(v.s, i)
Next


Sub setLong (array$, index, value&) ' Luke's Method except option explicit requires mod, no variables needed for one type
    If Len(array$) < 4 * (index + 1) Then
        array$ = array$ + String$(4 * (index + 1) - Len(array$), Chr$(0))
    End If
    Mid$(array$, index * 4 + 1) = _MK$(Long, value&)
End Sub

Function getLong& (array$, index)
    getLong& = _CV(Long, Mid$(array$, index * 4 + 1, 4))
End Function

I also want to add the way to do variable length string arrays is to put a demilter like a comma if text that does not use commas between each item then I use:

You build the string anyway that you need just putting delimiter between each items and pull items out by item number ( this is also base 1 stuff ie the first item has index 1).
Code: (Select All)
'Description: Use Item$() Function to treat strings like arrays without having to use an array structure.
' This function does not throw a fit if you ask for an item number (index) it does not have, it just returns an empty string.
' In QB64, Functions can't return arrays through the function name, but they can return strings that the Item$() function can
' translate like an an array index.  nItem numbers are the same as Counting numbers positive integers starting at 1.
'  eg Item7$ = Item$(CommaDelimitedString$, 7, ",") 'get 7th Item in string
Function Item$ (s$, nItem As Long, delimiter$)
    Dim c As Long, d As Long, lastd As Long
    If Len(s$) = 0 Then Item$ = "": Exit Function
    lastd = 1: d = InStr(lastd, s$, delimiter$)
    While d > 0
        c = c + 1
        If c = nItem Then
            Item$ = Mid$(s$, lastd, d - lastd): Exit Function
        Else
            lastd = d + 1: d = InStr(lastd, s$, delimiter$)
        End If
    Wend
    c = c + 1
    If c <> nItem Then Item$ = "" Else Item$ = Mid$(s$, lastd, Len(s$))
End Function