Rapid-Q Documentation by William Yu (c)1999 | Appendix C: User Defined Types |
TYPE TWorldAs you can see, you can have arrays within UDTs. You can also create arrays of UDTs:
CityName(100) AS STRING
Population(100) AS LONG
END TYPE
DIM World AS TWorld
TYPE TWorldThere is currently an issue with the memory address of your array (within UDT). Please note that this is hardly ever noticed unless you're going to pass one of these structures to a DLL that require arrays within UDTs. This address problem won't affect your program, but it's hard to explain (without getting too deep). NOTE: it only occurs when you create arrays of UDTs that have arrays within UDTs. In the first example (higher up), there is no address problem since you're not creating an array of UDTs. You can safely pass that to a DLL.
CityName(100) AS STRING
Population(100) AS LONG
END TYPE
DIM Country(100) AS TWorld
Sample code would use it something like this:
Country(1).CityName(15) = "Seattle"
Country(1).Population(15) = 2343589
What is not supported
Unlike other languages such as Visual Basic, Pascal, C/C++, you cannot
"nest" a UDT inside another UDT.
TYPE POINT
X as INTEGER
Y as INTEGER
END TYPE
TYPE Rectangle
Top AS POINT
Bottom AS POINT
'this won't work
END TYPE
'You would have to write the code this way...
TYPE Rectangle
TopX AS INTEGER
TopY AS INTEGER
BottomX AS INTEGER
BottomY AS INTEGER
END TYPE
Using this method, a problem is that you have to be careful not to repeat variable names from
the nested UDT. Unfortunately this makes for some messy code when working with header
files, Windows API calls, and and source code from other languages.
When you make a UDT and pass it to a DLL, you must have the UDT allocated in
memory contiguously. You can use QMEMORYSTREAM and its pointer to make
sure it is correctly formatted. You can try pass arrays of UDTs by writting multiple UDTs to a single
memory stream. Please see:
chapter9.html #97
TYPE dataType
x AS INTEGER '4 bytes
y AS INTEGER '4 bytes
z(300) AS SINGLE 'only 4 bytes allocated (just an array pointer?)
END TYPESo how do you store all the data in the array into one block of memory? The fix is to save the UDT to a QMEMORYSTREAM
DIM MyUDT AS dataType
DIM Mem AS QMemoryStream
Mem.WriteUDT(MyUDT)
'you can also copy the array to another arry if needed
Mem.position=8 'go to the start of the array
Mem.LoadArray(NewArray(3),13)Example#2 How to get parts of the UDT (also see help on "Pointers")
TYPE MyType
Name AS STRING*30
Phone AS STRING*8
Age AS INTEGER
z(1 to 3) AS SINGLE
END TYPE
DIM Person AS MyType
Person.Name = "Joe"
Person.Phone = "555-5555"
DIM Mem AS QMemoryStream
Mem.WriteUDT(Person)
ShowMessage str$(Mem.Pointer)
ShowMessage str$(sizeof(Person))
Person.z(1) = 2.23451
ShowMessage str$(Person.z(1))
showmessage str$(varptr(Person.Age)) 'this doesn't work
showmessage str$(@Person.Age) 'gets the value of the variable
'manually need to calculate offset!!!
ShowMessage str$(Mem.Pointer + 38 + SizeOf(INTEGER))
Variable length strings and Object pointers are not supported
Only fixed and simple data types (like INTEGER, SINGLE, LONG, STRING *12)
can be inside a UDT. For example, the next code sement will not compile..
TYPE MyUDT
a AS QRECT
b AS QD3DVector
c AS STRING
END TYPE
BUT if you define the type with EXTENDS it works...
TYPE newT EXTENDS QOBJECT
a AS QRECT
b AS QD3DVector
c AS STRING
END TYPE
Now your code runs ok, but you will probably run into bugs if you use the UDT methods:
Qmemorystream.ReadUDT(newT)
Qmemorystream.WriteUDT(newT)
--And you CAN'T make arrays of them --
DIM NewObj(1 to 100) as newT 'this
compiles
NewObj(1).left =
10 'compiler error
So how do you make an array of custom components? You can try QCANVAS instead.
It is the only Qcomponent that appears to allow arrays when EXTENDED.
$typecheck on
TYPE NewT EXTENDS QCANVAS
a as integer
b as string
c as Qd3dVector
d as Qbitmap
e as single
'd(1 to 5) as QBitmap
'can't have arrays of objects in an object
e(1 to 5) as
single
'errors occur with nested arrays however,
END TYPE
dim NewObj(1 to 6) as NewT
NewObj(2).a = 2
NewObj(2).b = "aaaaaaaaaaaaaaaaaahhh"
NewObj(1).c.x = 1
NewObj(1).c.dvx = 1
'NewObj(6).d(2).Width = 60 'bug
'NewObj(6).d(1).Height = 60 'bug
'NewObj(1).e(1) = 1.0009 'bug
NewObj(6).d.Width = 60
NewObj(6).d.Height = 60
NewObj(1).e = 1.0009
Still you can't use arrays inside custom components, you will have
to use the template like in this example (see the docs)
TYPE Arrays<DataType, Size> EXTENDS QOBJECT
ArrayItem(Size) AS DataType
and to call it in code
TYPE MyObject<SINGLE, Size> EXTENDS QOBJECT
ArrayItem(Size) AS DataType
... methods/subs/functions here
END TYPE
DIM myVar AS MyObject<SINGLE, 1000>
MyVar.ArrayItem(999) = 1.234
---------------------------------------
Copying UDTs
To copy 2 UDTs, just assign the two:
TYPE TStuffContents in S2 are copied to S1. When dealing with arrays within UDTs, the arrays are not copied, only referenced. To copy arrays of UDTs, you'll have to manually copy each one:
A AS LONG
B AS INTEGER
END TYPE
DIM S1 AS TStuff
DIM S2 AS TStuff
S1 = S2
DIM S1(100) AS TStuff
DIM S2(100) AS TStuff
FOR I = 1 TO 100
S1(I) = S2(I)
NEXT
Unhooking TYPE There are actually 2 ways to create your types, with TYPE and STRUCT. STRUCT is actually mirrored in TYPE, to unhook the two, you have to undefine TYPE.
$UNDEF TYPEUsing TYPE and STRUCT are not the same. If you unhook the two, TYPE now reverts back to the old style (pre-May 17 version), where arrays of UDTs is not allowed. Why the heck would I want a stripped down version? Well, perhaps performance. The old style TYPE lacks in many things, but makes up in performance, if that's really necessary.
TYPE TWorld
CityName(100) AS STRING
Population(100) AS LONG
END TYPE
DIM World AS TWorld
<- Branching | Contents | Conditions -> |