Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Arrays In User Data Types
#1
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.
Reply
#2
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.
Reply
#3
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
  724  855  599  923  575  468  400  206  147  564  878  823  652  556 bxor cross forever
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Mac debugger not connecting, a user error! BlameTroi 0 99 02-07-2026, 06:18 PM
Last Post: BlameTroi
Question Experimenting with a "StringList" type for simpler handling of string arrays/lists Heimdall 18 1,190 12-19-2025, 12:51 PM
Last Post: Heimdall
  Must an extra value be provided on DATA statement? dakra137 11 1,042 09-30-2025, 05:38 PM
Last Post: ahenry3068
  Seeking Matches in a Data Base Dimster 10 1,036 07-13-2025, 12:26 AM
Last Post: Dimster
  Question about _MEM blocks and arrays FCS_coder 19 2,618 06-15-2025, 10:09 AM
Last Post: TempodiBasic

Forum Jump:


Users browsing this thread: 1 Guest(s)