|
Post by Marco Kurvers on May 11, 2022 17:28:25 GMT -5
Hi Carl,
I use structs not only for API functions, but also for using variables in struct groups in the program.
Very nice to avoid variables with the global command. Thanks to the struct, you do not suffer from loose roaming variables. Therefore, my question is whether it is possible to use struct arrays without the use of API? A commonly used technique as we know a user defined type in Visual Basic.
I hope that you know what I mean.
Example in LB 4:
'dimensioneer de array met 100 adressen elements = 99 dim structArray(elements)
'definieer een structure struct test,_ a as char[20],_ b as long,_ c as char[20]
'de size van de structure is sizeofTest = len(test.struct)
'de geheugengrootte dat nodig is memBlockSize = (elements + 1) * sizeofTest
open "kernel32.dll" for DLL as #kernel
hSArray = GlobalAlloc(memBlockSize) ptrSarray = GlobalLock(hSArray)
'bijvoorbeeld, vul de hele array met structures for i = 0 to 99 'zet wat gegevens in de struct test.a.struct = "Carol - " + str$(i) test.b.struct = i test.c.struct = "Andy - " + str$(i)
'bereken het eindpunt als een offset vanaf de eerste byte dest = ptrSarray + (i * sizeofTest)
'plaats de structure in het geheugen calldll #kernel,"RtlMoveMemory", dest as long, test as ptr, sizeofTest as long, ret as void
'bewaar het adres zodat de gegevens gevonden kunnen worden wanneer ze nodig zijn structArray(i) = dest next i
'bijvoorbeeld, lees alles uit de structures for i = 0 to 99 test.struct = structArray(i) A$ = test.a.struct B = test.b.struct C$ = test.c.struct print A$ + " " + str$(B) + " " + C$ next i
'om het derde element van de 50ste structure te vinden: test.struct = structArray(49) C$ = test.c.struct print C$
'om de waarde van het derde element in de 50ste structure te wijzigen: test.struct = structArray(49) 'haal de structure test.c.struct = "Changed" 'wijzig één of meer waarden
'bewaar het dest = ptrSarray + (49 * sizeofTest) calldll #kernel,"RtlMoveMemory", dest as long, test as ptr, sizeofTest as long, ret as void
'het voorbeeld van de wijziging test.struct = structArray(49) C$ = test.c.struct print C$
input a$
[quit] ret = GlobalFree(hSArray) 'roep dit aan om elk gealloceerde blok vrij te geven.
'sluit de juiste geheugen handle. close #kernel end
'**** functies **** function GlobalAlloc(dwBytes) 'retourneert de handle van het nieuw gealloceerde geheugenobject. 'de returnwaarde is NULL als het faalt.
calldll #kernel, "GlobalAlloc", _GMEM_MOVEABLE as long, dwBytes as ulong, GlobalAlloc as long end function
function GlobalLock(hMem) 'retourneert een pointer naar de eerste byte van het geheugenblok. 'de returnwaarde is NULL als het faalt.
calldll #kernel, "GlobalLock", hMem as long, GlobalLock as long end function
function GlobalFree(hMem) calldll #kernel, "GlobalFree", hMem as ulong, GlobalFree as long end function
This should be shorter by saying: struct test(100), ...
Thanks, Marco
|
|
|
Post by Rod on May 12, 2022 2:20:05 GMT -5
While I see that Struct is important perhaps we could sweep all the complication aside by allowing arrays to be copied with one command. A$()=B$() or passed in to subs or functions as new arrays.
|
|
|
Post by Marco Kurvers on May 12, 2022 11:12:53 GMT -5
Yes, I know, but my idea is to use a record array with a struct in the same way as we can do with a record type in Pascal and in Visual Basic.
I think, it is better to have more Basic than to use API. But in such a way that the syntax remains Liberty BASIC.
|
|
|
Post by Brandon Parker on May 12, 2022 20:58:13 GMT -5
Well, if we do not end up getting the ability to create multiple structs based on a single struct definition, there might be a chance that creating it inside a Library might provide the desired functionality. Here is one way I could see it being defined in LB5 without any attempt at stuffing it into a Library: 'Create the template struct Struct myTemplateStruct, var1 As ptr, _ var2 As long
'Create a new struct from the existing struct myNewStruct = myTemplateStruct.struct
'Set the new struct's data myNewStruct.var1.struct = "Hello World!" myNewStruct.var2.struct = 5
'Print the new struct's data 'Maybe now we can drop the Winstring()... Print Winstring(myNewStruct.var1.struct) Print myNewStruct.var2.struct
'In this case, I think it would be prudent to 'be able to destroy struct objects created from 'the original template (maybe even the template as well). 'Something like this might work and be BASIC-like UnloadStruct myNewStruct.struct If you want this type of functionality in LB4.x.x, feel free to head over to my Import Architect Forum. {:0) Brandon Parker
|
|
|
Post by Carl Gundel on May 13, 2022 10:35:12 GMT -5
Structs are on the docket to be implemented in LB5 very soon. I am aiming for LB4 compatibility only for v5.0. Perhaps for v5.1 we will see something more. I am leaning more towards some more formal field based record feature, but these ideas are still not completely formed in my mind. Open to suggestions of course.
|
|
|
Post by Rod on May 13, 2022 10:59:45 GMT -5
Well as a dyed in the wool Basic guy I would just use a RAF.
|
|
|
Post by Walt Decker on May 13, 2022 11:39:38 GMT -5
By definition a structure is field based. Given: #DEFINE STRUCT MyStruct A AS LONG B AS DWORD Cs AS STRING * 128 Dz AS STIRINGZ * 128 Ewz AS WSTRINGZ * 256 Fptr AS POINTER QD AS QUAD Sng AS SINGLE Dbl AS DOUBLE Extn AS EXTENDED Flt AS FLOAT AnArray(100, 200) AS WORD
defines a template with each element(field) defined. The template itself takes up no memory. DIM MyFields AS MyStruct creates the structure in memory. Going one step farther: DIM MyFields(50) AS MyStruct would create an array of type MyStruct
Going one step farther, the structure itself can be used to read/write random access files.
|
|
|
Post by Rod on May 13, 2022 13:06:35 GMT -5
I struggle to see the difference between a struct and a RAF record.
So being VERY specific, educate me about the difference between a RAF record and a Struct. As I see it the RAF can do everything we have discussed.
|
|
|
Post by alincon on May 13, 2022 14:02:48 GMT -5
I would like to use a struct to read/write sequential files. Is that possible? r.m.
|
|
|
Post by Rod on May 13, 2022 14:24:02 GMT -5
A RAF is a sequential file that mimics a list of structs and you can read and write it. To use a Struct to fill it would require a second definition, the Struct, the filling of the Struct, and the passing of the Struct members to the RAF variables. A bit of duplication?
So why not plain vanilla RAF?
|
|
|
Post by Walt Decker on May 13, 2022 15:41:16 GMT -5
I would like to use a struct to read/write sequential files. Is that possible? r.m. No. By BASIC definition a sequential file is a file that has variable length records delimited by, depending on operation system, CRLF, CR, or LF. By definition in BASIC, C, Pascal, a structure is an ordered collection of bytes with STRICT field sizes.
By definition a file is just a collection of bytes. By using this definition you could output each element(field) of a structure to a file and append a CRLF to it. You would then have to read the data back in the exact order you output the data.
You are talking about two different things. Random Access File is a construction of BASIC. In reality it is just a collection of bytes. In BASIC one can open a file for "binary" and output a structure to it and, presto, you have an RAF. By knowing the size of the structure you can determine the size of the file and then read the data back in in any order you want.
On the other hand the structure resides in memory. If the structure is an array you can get the data by the index number of the array. Basically the same thing but much faster. To properly use structures in Liberty Basic to read/write RAF files the concept of RAF would have to be rethought since currently it writes everything out as ansi rather than what it really is.
|
|
|
Post by Chris Iverson on May 13, 2022 15:58:49 GMT -5
A RAF is a sequential file that mimics a list of structs and you can read and write it. To use a Struct to fill it would require a second definition, the Struct, the filling of the Struct, and the passing of the Struct members to the RAF variables. A bit of duplication? So why not plain vanilla RAF? Because you may not have been the one to define the format of the file you're working with. Say, for example, I wanted to parse a BMP file. That would require manually reading in, breaking down, and converting all the different parts of the header, to make sure you have the right info. And making sure you have it correct, even for different header versions. Or you could read the first (struct size) bytes in to the struct directly from the file, and the "conversion" is automatically done for you. struct BITMAPFILEHEADER,_ magic$ as char[2],_ bmpFileSize as ulong,_ reserved1 as short,_ reserved2 as short,_ bmpDataStart as ulong
struct BITMAPINFOHEADER,_ biSize as ulong, biWidth as long,_ biHeight as long, biPlanes as short,_ biBitCount as short, biCompression as ulong,_ biSizeImage as ulong, biXPelsPerMeter as long,_ biYPelsPerMeter as long, biClrUsed as ulong,_ biClrImportant as ulong
file$ = "bmp\piano6.BMP" 'file$ = "C:\temp\temp.bmp"
callDLL #kernel32, "CreateFileA",_ file$ as ptr, _GENERIC_READ as long,_ _FILE_SHARE_READ as long, 0 as long,_ _OPEN_EXISTING as long, _FILE_ATTRIBUTE_NORMAL as long,_ 0 as long, hFile as ulong
print "hFile - ";hFile if hFile = 0 then goto [end]
struct a, bytesRead as ulong lenBMFH = len(BITMAPFILEHEADER.struct) callDLL #kernel32, "ReadFile",_ hFile as ulong, BITMAPFILEHEADER as struct,_ lenBMFH as ulong,_ a as struct, 0 as long,_ ret as long
print "ReadFile ret - ";ret
print BITMAPFILEHEADER.magic$.struct print BITMAPFILEHEADER.bmpFileSize.struct print BITMAPFILEHEADER.bmpDataStart.struct print
lenBMIH = len(BITMAPINFOHEADER.struct)
callDLL #kernel32, "ReadFile",_ hFile as ulong, BITMAPINFOHEADER as struct,_ lenBMIH as ulong,_ a as struct, 0 as long,_ ret as long
print "ReadFile ret - ";ret print BITMAPINFOHEADER.biSize.struct print BITMAPINFOHEADER.biWidth.struct print BITMAPINFOHEADER.biHeight.struct print BITMAPINFOHEADER.biBitCount.struct print BITMAPINFOHEADER.biClrUsed.struct
[end] calldll #kernel32, "CloseHandle",_ hFile as ulong, ret as long Doing this won't work if you try to "use" it like a one-record-length RAF file, because LB converts everything to/from strings when serializing/unserializing it for an RAF file.
|
|
|
Post by alincon on May 14, 2022 7:46:22 GMT -5
Walt: I have sequential files that have non-variable length records. Why can't I read them one at a time into a structure? (like I do when reading random files sequentialy)
Rod: You said an RAF file is a sequential file? RAF does not stand for random access file?
r.m.
|
|
|
Post by Walt Decker on May 14, 2022 8:53:56 GMT -5
#DEFINE STRUCT MyStruct A AS LONG B AS LONG C AS STRING * 128 #END STRUCT In terms of RAF the above is one record. A, B, C are elements(fields) of that record. In terms of sequential files A, B, C are records defined by CRLF. Within A, B, C there may be elements(fields) defined by some arbitrary delimiter. You might be able to use LB's INPUTCSV instruction to read the elements(fields) of the record, but you will have to put them in a string first then write the string into the appropriate struct element provided the data type matches and strings are of no greater length than that defined in the structure. If the data type does not match conversion is necessary. Bottom line is, you can put the data into a structure but not directly.
PS: In LBPRO there is no provision for writing a structure directly to an RAF or reading data of an RAF into a structure. I have no idea what is going on with LB5.
|
|
|
Post by Carl Gundel on May 14, 2022 9:22:05 GMT -5
Walt: I have sequential files that have non-variable length records. Why can't I read them one at a time into a structure? (like I do when reading random files sequentialy) Rod: You said an RAF file is a sequential file? RAF does not stand for random access file? The trouble with using a STRUCT to read and write a file is that some of the elements of the STRUCT might be pointers. We could resolve those to what they point to when we GET and PUT them, but you will need to be careful about the length of strings and other data and manage all that in your code. I would probably be better just to extend/enhance the current random access file mechanisms. I am guessing that the * 125 part of Walt's example is meant to manage the limits of a datum that is referred to by a pointer. Seems like a reasonable way to approach it, but things might get confusing if we decide that our STRUCT should have pointers to other STRUCTs, etc. and how does this map into a random access file? #DEFINE STRUCT MyStruct A AS LONG B AS LONG C AS STRING * 128 #END STRUCT Some sort of well thought out fielded RECORD feature might be easier for most people wrap their minds around.
|
|