• Welcome to Jose's Read Only Forum 2023.
 

Dynamic strings in TYPE structures

Started by Paul Squires, August 06, 2007, 05:44:37 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Paul Squires




'  Demonstrate one way of creating dynamic strings in TYPE structures.

#Compile Exe

#Include "win32api.inc"


Type CUSTOMER_TYPE
   nCustID    As Long
   zCustName  As Asciiz Ptr   ' dynamic string
   zAddress   As Asciiz Ptr   ' dynamic string
   nUnpaid    As Single
   nActive    As Byte
End Type



'//
'//  Manually allocate a block of memory at the specified location
'//
Function MemAlloc(ByVal nSize As Long) As Dword
   If nSize > 0 Then
      Function = HeapAlloc( GetProcessHeap(), %HEAP_ZERO_MEMORY, nSize)
   End If
End Function


'//
'//  Free memory located at a specified location
'//
Function MemFree(ByRef pMem As Dword) As Long
   If pMem Then
      Function = HeapFree( GetProcessHeap(), 0, ByVal pMem)
      pMem = 0
   End If
End Function


'//
'//  Sub that will assign a dynamic string to a memory location
'//  and free any memory that may have already been allocated.
'//  pMem must be ByRef.
'//
Sub MemString( ByRef pMem As Dword, _
               ByVal sData As String )

    If pMem Then HeapFree( GetProcessHeap(), 0, ByVal pMem)  ' free any existing memory
    sData = sData & $Nul
    pMem = HeapAlloc( GetProcessHeap(), %HEAP_ZERO_MEMORY, Len( sData ) + 1)
    If pMem Then Poke$ pMem, sData

End Sub





Function PBMain() As Long


'-------------------------------------------------------------------------
' (1)  Using manual memory allocation to manage dynamic strings in TYPE's.
'-------------------------------------------------------------------------

   ' Assign some default values to a TYPE structure
   Local cust As CUSTOMER_TYPE
   
   cust.nCustID = 1000
   cust.nUnpaid = 2300.00
   cust.nActive = %TRUE
   MemString cust.zCustName, "Paul Squires"
   MemString cust.zAddress,  "SomeTown, Anywhere, 12345"
                               
                               
   ' Display the record
   MsgBox "nCustID   = " & Format$(cust.nCustID)                & $CrLf & _
          "nUnpaid   = " & Format$(cust.nUnpaid, "######.00")   & $CrLf & _
          "nActive   = " & IIf$(cust.nActive, "TRUE", "FALSE" ) & $CrLf & _
          "zCustName = " & cust.@zCustName & $CrLf & _
          "zAddress  = " & cust.@zAddress
                               
                               
   ' Modify the name and address.
   ' The functions take care of freeing the original string memory
   ' and then creating a new string.
   MemString cust.zCustName, "Paul Squires (PlanetSquires)"
   MemString cust.zAddress,  "MyTown, CloseBy, 55555"
     
     
   ' Display the modified record
   MsgBox "nCustID   = " & Format$(cust.nCustID)                & $CrLf & _
          "nUnpaid   = " & Format$(cust.nUnpaid, "######.00")   & $CrLf & _
          "nActive   = " & IIf$(cust.nActive, "TRUE", "FALSE" ) & $CrLf & _
          "zCustName = " & cust.@zCustName & $CrLf & _
          "zAddress  = " & cust.@zAddress

   
   ' Free the allocated memory for the dynamic strings.
   MemFree cust.zCustName
   MemFree cust.zAddress                                         
   
   

'-------------------------------------------------------------------------
' (2)  Let's get fancy... Create the TYPE structure itself dynamically
'      and add some dynamic strings to it.
'-------------------------------------------------------------------------
   
   Local pCust As CUSTOMER_TYPE Ptr
   
   pCust = MemAlloc( SizeOf(CUSTOMER_TYPE) )
   
   @pCust.nCustID = 5000
   @pCust.nUnpaid = 9000.00
   @pCust.nActive = %TRUE
   MemString @pCust.zCustName, "Jose Roca"
   MemString @pCust.zAddress,  "Across the Pond, OverThere, 919191"
                               
                               
   ' Display the record
   MsgBox "nCustID   = " & Format$(@pCust.nCustID)                & $CrLf & _
          "nUnpaid   = " & Format$(@pCust.nUnpaid, "######.00")   & $CrLf & _
          "nActive   = " & IIf$(@pCust.nActive, "TRUE", "FALSE" ) & $CrLf & _
          "zCustName = " & @pCust.@zCustName & $CrLf & _
          "zAddress  = " & @pCust.@zAddress


   ' Free the allocated memory for the dynamic strings. Not really
   ' necessary at this point because the application is about to
   ' terminate and Windows will reclaim the memory automatically, but
   ' it is good programming practice nonetheless.
   MemFree @pCust.zCustName
   MemFree @pCust.zAddress                                         
   
   ' Also free the dynamically created TYPE (pCust) itself
   MemFree pCust
   
End Function



Paul Squires
FireFly Visual Designer SQLitening Database System JellyFish Pro Editor
http://www.planetsquires.com

Donald Darden

I think we should clarify here that any PTR reference does not define string or any
other type of space in memory.  It can only be made to point to existing structures,.  Strings are only allocated when actually defined somewhere, at which
point you can establish a pointer reference to them.  So a TYPE structure can
either contain strings which are allocated when you DIM a variable of that type,
or if they have string pointers as you advise, those strings must be allocated elsewhere, since only the 32-bit pointer reference will be allocated in the new
variable of that type.  And the pointer references will be set to NUL until such
time as you create a reference to the specific string in question.  Firther, you
specified here that these are pointers to ASCIIZ strings, which have a maximum
length specified, but you specifically mentioned dynamic strings, which one would think of as the dynamic, variable length variety.  That's a different anamal.  If you set the PTR references to the string locations for this type of string, you are at risk of the STRPTR() value becoming invalid, because the Heap
where these strings are stored is constantly being released and reallocated as
other strings have their contents changed or discarded.  For these strings, you
might better use VARPTR(), which will remain constant, and which will give you
access to both the location of the string contents and the length (number of
characters currently allocated to the string).

Eros Olmi

#2
PTR to ASCIIZ are bad beats. They can produce unwanted behave if user is not conscious of what he/she is doing. There are also some limitations if that pointer is passed to something expecting big dynamic string because ASCIIZ strings are limited to about 16Mb. Also user must be aware he/she cannot embed NULL chars as real chars. If user is all aware, than no problem.

A different approach can be the one I suggested in another post that is to use just a LONG and using PB power to do the hard work:

'---Define some LONGs in place of strings
type MyUDT
  aString1 as long
  aString2 as long
end type

'---Define a UDT variable
dim MyVar as MyUDT

'---Define a dummy string that in reality is a place holder pointer pointing to the LONG inside the UDT
dim DummyString as string at varptr(MyVar.aString1)

'---Do what you need with DummyString like any other standard dynamic string
DummyString = "abcdef"
DummyString = repeat$(10, DummyString)
...

'---The bad part, free allocated string data manually
remove MyString


In this case DIM ... AT will make the hard work. Programmer have to just deal with deallocation.

Also

REDIM DummyString(1& to 1&) AS STRING AT VARPTR(MyVar.aString1)

can be used when accessing different string dynamically because you can use REDIM ... AT many times in code always with the same DummyString name but at different mem locations.

ADDED: I've not made any speed test of using DIM or REDIM ... AT. Maybe worth to check how much CPU is taken when making a lot of REDIM ... AT

Ciao
Eros
thinBasic Script Interpreter - www.thinbasic.com | www.thinbasic.com/community
Win7Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

Petr Schreiber

Hi Eros,

this looks like a good approach !
DIM .. AT is very powerful.


Thanks,
Petr
AMD Sempron 3400+ | 1GB RAM @ 533MHz | GeForce 6200 / GeForce 9500GT | 32bit Windows XP SP3

psch.thinbasic.com

Eros Olmi

The most interesting part of this approach is that after DIM or REDIM ... AT, the string is a 100% dynamic string
It can be used in any place a dynamic string is used in exact the same way as a standard dynamic string, passed BYREF or BYVAL.
Working with pointers on such created strings will follow the same rules as a standard dynamic string.

In any case, every approach is interesting. It depends on what you need to do. Than keep the best fits your needs.
Personally I do not like to deal with ASCIIZ strings because of the limitations they have especially on NULL chars. But this is just my personal approach.

Eros
thinBasic Script Interpreter - www.thinbasic.com | www.thinbasic.com/community
Win7Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB

Paul Squires

I guess my use of "dynamic string" is a little misleading. I am not referring to an OLE string but rather the fact that you can create strings in a TYPE that need not be of one pre-defined length. I guess that embedded Nulls would be an issue but for my uses I have never had to deal with them so I have always stuck with my code above.

I could have used the OLE engine directly:


#Compile Exe

#Include "win32api.inc"                   


Function PBMain() As Long                   

   Local st    As String
   Local pStr  As Asciiz Ptr
   
   st = "Paul Squires"
   
   ' Example of using the OLE dynamic string functions. Be aware
   ' of the ByVal's... they are needed.
   pStr = SysAllocStringByteLen( ByCopy st, ByVal Len(st) )
   
   MsgBox "pStr="   & Format$(pStr) & $CrLf & _
          "len="    & Format$(SysStringByteLen( ByVal pStr )) & $CrLf & _
          "String=" & @pStr & $CrLf & _
          "len="    & Format$(Len(@pStr))
   
   ' Free the string
   SysFreeString ByVal pStr
   
   ' Error <> 0 indicates that string was not freed.
   MsgBox "String freed. GetLastError=" & Format$(GetLastError)
   
End Function




Paul Squires
FireFly Visual Designer SQLitening Database System JellyFish Pro Editor
http://www.planetsquires.com

Eros Olmi

No problem here. For me it was clear what "dynamic" stand for in this post.
Every technique is really interesting because it shows the many different ways things can be done.

Maybe a little confusing for those start programming of for those gui that have never deal with pointers.

Thanks a lot
Eros
thinBasic Script Interpreter - www.thinbasic.com | www.thinbasic.com/community
Win7Pro 64bit - 8GB Ram - Intel i7 M620 2.67GHz - NVIDIA Quadro FX1800M 1GB