Auto-Sized Zstring Object

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Auto-Sized Zstring Object

Post by fxm »

Code: Select all

' Auto-Sized Zstring Object
'
' Structure 'ZstringObject' defining a zstring object where memory allocation for the zstring is automatically handled:
'    - A private member zstring descriptor is used, containing:
'       - address of the character data of the string,
'       - size of allocated memory.
'    - Different public member procedures are defined:
'       - default constructor,
'       - conversion constructor (from zstring),
'       - copy constructor (from another object),
'       - conversion let operator (from zstring),
'       - copy let operator (from another object),
'       - cast operator (to zstring),
'       - string index operator (to ubyte),
'       - length property,
'       - size property,
'       - zstrptr property,
'       - destroy sub,
'       - destructor.
'    - [Re]Allocation of memory (sized for the new zstring plus 12.5%):
'       - if the required memory size is greater than the already allocated memory size,
'       - or if the required memory size is less than the already allocated memory size minus 25%.
'    - Note:
'       - '@ZstringObject_instance' does not point to the character data of the string,
'       - but it points to the member data of 'ZstringObject' corresponding to its zstring descriptor.

Type ZstringObject Extends Zstring
    Public:
        Declare Constructor()
        Declare Constructor(Byref z As Zstring)
        Declare Constructor(Byref zo As ZstringObject)
        Declare Operator Let(Byref z As Zstring)
        Declare Operator Let(byref zo As ZstringObject)
        Declare Operator Cast() Byref As Const Zstring
        Declare Operator [](Byval n As Integer) Byref As Ubyte
        Declare Property Length() As Integer
        Declare Property Size() As Integer
        Declare Property ZstrPtr() As Zstring Const Ptr
        Declare Sub Destroy()
        Declare Destructor()
    Private:
        Dim As Zstring Ptr pdatazstring
        Dim As Integer memoryallocated
End Type

Constructor ZstringObject()
    This.destroy()
End Constructor

Constructor ZstringObject(Byref z As Zstring)
    This = z  '' using 'Operator Let(Byref As Zstring)'
End Constructor

Constructor ZstringObject(Byref zo As ZstringObject)
    This = zo  '' using 'Operator Let(byref As ZstringObject)'
End Constructor

Operator ZstringObject.Let(Byref z As Zstring)
    If This.pdatazstring <> @z Then     '' avoid self assignment destroying the chain
        Dim As Integer lm = Len(z) + 1  '' '+ 1' advised for zstring character data
        If (lm > This.memoryallocated) Or (lm < This.memoryallocated - This.memoryallocated Shr 2) Then
            This.destroy()
            This.memoryallocated = lm + lm Shr 3
            This.pdatazstring = Callocate(This.memoryallocated)
        End If
        *This.pdatazstring = z
    End If
End Operator

Operator ZstringObject.Let (byref zo As ZstringObject)
    This = *zo.pdatazstring  '' using 'Operator Let(Byref As Zstring)'
End Operator

Operator ZstringObject.Cast() Byref As Const Zstring
    Return *This.pdatazstring
End Operator

Operator ZstringObject.[](Byval n As Integer) Byref As Ubyte
    Return This.pdatazstring[n]  '' inplicit conversion from Zstring to Ubyte
End Operator

Property ZstringObject.Length() As Integer
    Return Len(*This.pdatazstring)
End Property

Property ZstringObject.Size() As Integer
    Return This.memoryallocated
End Property

Property ZstringObject.ZstrPtr() As Zstring Const Ptr
    Return This.pdatazstring
End Property

Sub ZstringObject.Destroy()
    If This.pdatazstring <> 0 then
        Deallocate This.pdatazstring
        This.pdatazstring = 0
    End If
End Sub

Destructor ZstringObject()
    This.destroy()
End Destructor

'---------------------------------------------------------------------------------------------------------

Scope
    
    Dim zo1 As ZstringObject = ZstringObject("123")  '' using 'Constructor(Byref As Zstring)'
    Print zo1                                        '' using 'Operator Cast() Byref As Const Zstring'
    
    Dim zo2 As ZstringObject                         '' using 'Constructor()'
    zo2 = ZstringObject("1234567")                   '' using 'Constructor(Byref As Zstring)'
'                                                           + 'Operator Let(Byref As ZstringObject)'
'                                                           + 'Destructor()'
    Print zo2                                        '' using 'Operator Cast() Byref As Const Zstring'

    Dim zo3 As ZstringObject = ZstringObject(zo1)    '' using 'Constructor(Byref As ZstringObject)'
    Print zo3                                        '' using 'Operator Cast() Byref As Const Zstring'

    zo2 = zo1                                        '' using 'Operator Let(Byref As ZstringObject)'
    Print zo2                                        '' using 'Operator Cast() Byref As Const Zstring'

    zo2 = zo3                                        '' using 'Operator Let(Byref As ZstringObject)'
    Print zo2                                        '' using 'Operator Cast() Byref As Const Zstring'

    zo2 = "123456789"                                '' using 'Operator let(Byref As Zstring)'
    Print zo2 & "   ";                               '' using 'Operator Cast() Byref As Zstring'
    For I As Integer = 0 To zo2.Length - 1           '' using 'Property Length() As Integer'
        Print zo2[i] & "   ";                        '' using 'Operator [](Byval n As Integer) Byref As Ubyte'
    Next I
    Print
    
    Dim zo4 As ZstringObject                         '' using 'Constructor()'
    zo4 = zo1 + zo2 & zo3                            '' using 'Operator Cast() Byref As Const Zstring' x 3
'                                                           + 'Operator ZstringObject.Let(Byref z As Zstring)'
    Print zo4                                        '' using 'Operator Cast() Byref As Const Zstring'
    
    zo4 = zo2                                        '' using 'Operator Let(Byref As ZstringObject)'
    Print *zo4.ZstrPtr & "   ";                      '' using 'Property ZstrPtr() As Zstring Const Ptr'
    Print zo4.Length & "   ";                        '' using 'Property Length() As Integer'
    Print zo4.Size                                   '' using 'Property Size() As Integer'

End Scope                                            '' using 'Destructor()' x 4

Sleep
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Auto-Sized Zstring Object

Post by fxm »

Note:
If the 'cast operator (to zstring) + conversion constructor (from zstring)' combination allows to clone an object (as code in the above post), then the copy constructor (from another object) is not mandatory (because substituted by the combination above), but the copy let operator (from another object) is mandatory (otherwise substituted by the implicit copy let operator which is a simple shallow copy).

The above structure corresponds to a an almost minimal structure to define an auto-sized zstring object:
- One could for example have added the overload operators '+' and '&', instead of using by implicit conversion the cast operator (to zstring) and the conversion let operator (from zstring),
- but it will not improve the execution time, but on the contrary will penalize it a little I think..

However, for adding the overload operators '+' and '&':

Code: Select all

Operator +(Byref z As Zstring, Byref zo As ZstringObject) As ZstringObject
    Return z + *zo.ZstrPtr
End Operator
    
Operator +(Byref zo As ZstringObject, Byref z As Zstring) As ZstringObject
    Return *zo.ZstrPtr + z
End Operator
    
Operator +(Byref zo1 As ZstringObject, Byref zo2 As ZstringObject) As ZstringObject
    Return *zo1.ZstrPtr + *zo2.ZstrPtr
End Operator
    
Operator &(Byref z As Zstring, Byref zo As ZstringObject) As ZstringObject
    Return z & *zo.ZstrPtr
End Operator
    
Operator &(Byref zo As ZstringObject, Byref z As Zstring) As ZstringObject
    Return *zo.ZstrPtr & z
End Operator
    
Operator &(Byref zo1 As ZstringObject, Byref zo2 As ZstringObject) As ZstringObject
    Return *zo1.ZstrPtr & *zo2.ZstrPtr
End Operator
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Auto-Sized Zstring Object

Post by dodicat »

Doesn't all this just boil down to a zstring ptr?
The oop perhaps makes the working code a little bit more readable (and perhaps writeable), but I can see no obvious other advantage, although I am probably missing something!
And the constructors + let operators (in general) with memory allocation/deallocation makes fb oop very cryptic to use unless you are a real expert.
For a general foot soldier like myself, if I was to plan a large project, I would avoid these things, although I would be happy enough to use types + fields+methods.

But is is good to see that very old code written years ago mostly runs straight off with the latest build. (_latest.exe)

Code: Select all


type ZstringObject as zstring ptr

Scope
    
    Dim zo1 As ZstringObject = strptr("123")  
    Print *zo1                               
   
    Dim zo2 As ZstringObject                    
    zo2 = strptr("1234567")      
'                                                
    Print *zo2                          

    Dim zo3 As ZstringObject = zo1   
    Print *zo3                         

    zo2 = zo1                               
    Print *zo2                    

    zo2 = zo3                                   
    Print *zo3                     

    zo2 = strptr("123456789")              
    Print *zo2  & "   ";                   

    For I As Integer = 0 To len(*zo2)-1
        Print (@(*zo2)[0])[i] & "   ";
    Next I
    Print
    
    Dim zo4 As ZstringObject 
    var tmp = *zo1 + *zo2 & *zo3   
    zo4=strptr(tmp)

    Print *zo4            
    
    zo4 = zo2 
  
    Print *zo4 & "   ";               
    Print len(*zo4) & "   ";               
                          
End Scope                                

Sleep 
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Auto-Sized Zstring Object

Post by fxm »

dodicat wrote: Jan 29, 2023 14:03 And the constructors + let operators (in general) with memory allocation/deallocation makes fb oop very cryptic to use unless you are a real expert.

But once the structure is written, it is more easy to use:

Code: Select all

Dim zo4 As ZstringObject
zo4 = zo1 + zo2 & zo3
Print zo4
is simpler than:

Code: Select all

Dim zo4 As ZstringObject 
var tmp = *zo1 + *zo2 & *zo3   
zo4=strptr(tmp)
Print *zo4
But if you want to stick to the 1980s (like Quick Basic), it is everyone is choice.

But I see you also like a bit of complexity for nothing:

Code: Select all

Print (@(*zo2)[0])[i] & "   ";
instead of:

Code: Select all

Print (*zo2)[i] & "   ";
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Auto-Sized Zstring Object

Post by fxm »

dodicat wrote: Jan 29, 2023 14:03 And the constructors + let operators (in general) with memory allocation/deallocation makes fb oop very cryptic to use unless you are a real expert.

To know which member procedures should be defined (without knowing the complex substitution rules), I proposed to systematically apply my maximizing rule which can be found in the Programmer's Guide / User Define Types / Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2):
Paragraph "Rules of good manners (for constructors, copy-constructors, copy-assignment operators, and destructors)":
Maximizing rule for a very safer code (at compile-time and run-time), but sometimes maximizing the real constraints:
- If the user needs to explicitly define any form of constructor procedure (including any form of copy-constructor) or any form of let-operator procedure or a destructor procedure, it is strongly recommended to define together the default-constructor and the standard copy-constructor and the standard let-operator and the destructor.
(these 4 explicit procedures are explicitly defined to always overload correctly the corresponding implicit operations from compiler)
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Auto-Sized Zstring Object

Post by dodicat »

Thanks fxm.
You say that using

Code: Select all

Print (@(*zo2)[0])[i] & "   ";   

is a complexity, agreed for a simple zstring.

Here I use two udt's.
ZstringObject, which is very simple, a zstring ptr, one method and a cast() to easily print a result.
I know you found problems with cast() recently, but I don't think it applies here.

ZstringObject2 which uses the basic constructors and a let operator along with a zstring ptr, one method and a cast() .
Also operator[] as a test.
A set up like you say could be more convenient (OOP like).

Print (@(*zo2)[0]) & " "; should produce identical results.
(I don't allocate/deallocate/destructor)
Two scope blocks with a loop in each.


Code: Select all


'#cmdline "-exx"
'=======================================================================
Type ZstringObject
      Dim As Zstring Ptr pdatazstring
      Declare Operator Cast() Byref As zstring
      Declare Sub increment()
End Type

Sub ZstringObject.increment()
      Static As String s
      s=*pdatazstring
      Var ls=Len(s),counts=0
      Do
            If  s[ls-counts-1]=57 Then
                  counts=counts+1
                  If counts=ls Then s="1"+String(ls,"0"):Exit Do
            Else
                  s=Left(s,ls-counts-1)+Str(s[ls-counts-1]-47)+String(counts,"0")
                  Exit Do
            End If
      Loop 
      this.pdatazstring=(Strptr(s))
End Sub


Operator ZstringObject.cast() Byref As zstring
Return *pdatazstring
End Operator

Operator +(a As ZstringObject,b As ZstringObject) As ZstringObject
static as string s
 s=*a.pdatazstring + *b.pdatazstring
dim as ZstringObject ret=type(strptr(s))
Return ret
End Operator

Operator &(a As ZstringObject,b As ZstringObject) As ZstringObject
Return a+b
End Operator

'==============================================================================
'==============================================================================


Type ZstringObject2
      Dim As Zstring Ptr pdatazstring
      declare constructor
      declare constructor(as zstring)
      declare operator let(as zstring)
      Declare Operator Cast() Byref As zstring
      Declare operator[](as integer) byref as ubyte
      Declare Sub increment()
End Type

Operator ZstringObject2.[](n As Integer) Byref As Ubyte
    Return pdatazstring[n] 
End Operator

constructor ZstringObject2
end constructor

constructor ZstringObject2(n as zstring)
pdatazstring = strptr(n)
end constructor

operator ZstringObject2.let(n as zstring)
pdatazstring = strptr(n)
end operator

Sub ZstringObject2.increment()
      Static As String s
      s=*pdatazstring
      Var ls=Len(s),counts=0
      Do
            If  s[ls-counts-1]=57 Then
                  counts=counts+1
                  If counts=ls Then s="1"+String(ls,"0"):Exit Do
            Else
                  s=Left(s,ls-counts-1)+Str(s[ls-counts-1]-47)+String(counts,"0")
                  Exit Do
            End If
      Loop
      this=s 'via constructor (optional)
      'this.pdatazstring=(Strptr(s))
End Sub


Operator ZstringObject2.cast() Byref As zstring
Return *pdatazstring
End Operator

Operator +(a As ZstringObject2,b As ZstringObject2) As ZstringObject2
static as string s
 s=*a.pdatazstring + *b.pdatazstring
dim as ZstringObject2 ret=s
Return ret
End Operator

Operator &(a As ZstringObject2,b As ZstringObject2) As ZstringObject2
Return a+b
End Operator
'=============================================================================


scope
Dim As ZstringObject x=(Strptr("0"))
dim as double t=timer
Do
     
      Locate 2,,0
      x.increment
            Print  "'";x;"'" ;"  <------ gets to 1500" 
            Dim As String g="123"
            
            Dim zo1 As ZstringObject = (Strptr(g))  
            Print zo1                                
            
            Dim zo2 As ZstringObject                    
            zo2 =Type(Strptr("1234567"))     
                                                            
            Print zo2                           
            
            Dim zo3 As ZstringObject = zo1   
            Print zo3                         
            
            zo2 = zo1                               
            Print zo2                    
            
            zo2 = zo3                                   
            Print zo3                    
            
            zo2 = Type(Strptr("123456789"))              
            Print zo2  & "   ";                   
            
            For I As Integer = 0 To Len(zo2)-1
                  Print (@(*zo2.pdatazstring)[0])[i] & "   ";
            Next I
            Print
            
            Dim zo4 As ZstringObject 
           
            zo4=zo1+zo2 & zo3
            
            Print zo4            
            
            zo4 = zo2 
            
            Print *zo4.pdatazstring & "   ";               
            Print Len(*zo4.pdatazstring) & "   ";               
            
            Print
      
Loop until x="1500"
 print timer-t; " seconds"
end scope

'----------------------------------------------------------------------

scope
   Dim As ZstringObject2 x="0"
   dim as double t=timer
   do
    Locate 15,,0
    x.increment
            Print  "'";x;"'";"   <------ gets to 1500" 
            Dim As String g="123"
            
            Dim zo1 As ZstringObject2 = g 
            Print zo1                                
            
            Dim zo2 As ZstringObject2                    
            zo2 ="1234567"   
                                                            
            Print zo2                           
            
            Dim zo3 As ZstringObject2 = zo1   
            Print zo3                         
            
            zo2 = zo1                               
            Print zo2                    
            
            zo2 = zo3                                   
            Print zo3                    
            
            zo2 = "123456789"              
            Print zo2  & "   ";                   
            
            For I As Integer = 0 To Len(zo2)-1
                  'Print zo2[i] & "   "; 
                  Print (@(*zo2.pdatazstring)[0])[i] & "   ";
            Next I
            Print
            
            Dim zo4 As ZstringObject2 
            
            zo4=zo1+zo2 & zo3
            
            Print *zo4.pdatazstring             
            
            zo4 = zo2 
            
            Print *zo4.pdatazstring & "   ";               
            Print Len(*zo4.pdatazstring) & "   ";               
            
            Print
            
Loop until x="1500"     
 print timer-t; " seconds"        
  print
  print "Done"
   
      end scope
Sleep
 
Last edited by dodicat on Jan 31, 2023 15:12, edited 1 time in total.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Auto-Sized Zstring Object

Post by fxm »

Your "+" operator cannot work as coded, even with its body typo corrected ('*temp.pdatazstring=*a.pdatazstring+*b.pdatazstring'):

Code: Select all

Scope
    
    Dim zo1 As ZstringObject2 = "123"
    Print zo1
    
    Dim zo2 As ZstringObject2 = "1234567"
    Print zo2

    Dim zo3 As ZstringObject2
    zo3 = zo1 + zo2
    Print zo3
    
End Scope

Each time the operator is called, a new permanent string must be created, like for example:

Code: Select all

Operator +(a As ZstringObject2,b As ZstringObject2) As ZstringObject2
Dim As String Ptr ps = New String
*ps=*a.pdatazstring+*b.pdatazstring
Return *strptr(*ps)
End Operator
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Auto-Sized Zstring Object

Post by dodicat »

Thanks, fixed that silly error.
All seems OK now, both results match.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Auto-Sized Zstring Object

Post by fxm »

Defining a static string does not work as soon as the operator is called at least 2 times.
Try:

Code: Select all

Scope
    
    Dim zo1 As ZstringObject2 = "123"
    Print zo1
    
    Dim zo2 As ZstringObject2 = "1234567"
    Print zo2

    Dim zo3 As ZstringObject2
    zo3 = zo1 + zo2
    Print zo3
    
    Dim zo4 As ZstringObject2
    zo4 = zo3 + zo2
    Print zo4
    
    Print zo3  '' does not work
    
End Scope
It is why I wrote:
fxm wrote: Jan 31, 2023 13:12 Each time the operator is called, a new permanent string must be created, like for example:

Code: Select all

Operator +(a As ZstringObject2,b As ZstringObject2) As ZstringObject2
Dim As String Ptr ps = New String
*ps=*a.pdatazstring+*b.pdatazstring
Return *strptr(*ps)
End Operator
Lost Zergling
Posts: 534
Joined: Dec 02, 2011 22:51
Location: France

Re: Auto-Sized Zstring Object

Post by Lost Zergling »

Maybe one could imagine static string passed byref, as an additionnal operator parameter ? I did not try doing it.
My point is not trying to oppose object vs classic ptr. Both cons and advantages.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Auto-Sized Zstring Object

Post by dodicat »

fxm's new string method gobbles memory in a loop.
Instead I use a static array big enough to hold all calls to the + operator in a loop, and then a few more for good measure, and store into the array element by element.
This should provide unique locations for data,
Resource monitor will show.
Choice
1) arraymethod
2) newstringmethod
Test code

Code: Select all


Type ZstringObject 
      Dim As Zstring Ptr pdatazstring
      Declare Operator Cast() Byref As zstring
      Declare Sub increment()
End Type


Sub ZstringObject.increment()
      Static As String s
      s=*pdatazstring
      Var ls=Len(s),counts=0
      Do
            If  s[ls-counts-1]=57 Then
                  counts=counts+1
                  If counts=ls Then s="1"+String(ls,"0"):Exit Do
            Else
                  s=Left(s,ls-counts-1)+Str(s[ls-counts-1]-47)+String(counts,"0")
                  Exit Do
            End If
      Loop 
      this.pdatazstring=(Strptr(s))
End Sub

Operator ZstringObject.cast() Byref As zstring
Return *pdatazstring
End Operator


#macro newstringmethod
Operator +(a As ZstringObject,b As ZstringObject) As ZstringObject
Dim As String ptr ps = New String
*ps=*a.pdatazstring+*b.pdatazstring
Return type(strptr(*ps))'*strptr(*ps)
End Operator
#endmacro

#macro arraymethod
Operator +(a As ZstringObject,b As ZstringObject) As ZstringObject
static as string s(1 to 1000)
static as long k
k+=1
if k>ubound(s) then k=1
 s(k)=*a.pdatazstring + *b.pdatazstring
Return type(strptr(s(k)))
End Operator
#endmacro


dim as string big=string(100000,"Z")
Dim As ZstringObject x=(Strptr("0"))


arraymethod
'newstringmethod



do
      
      Locate 2,,0
      x.increment
            Print  "'";x;"'" ;"  <------ gets to 1500" 
Scope
    
    Dim zo1 As ZstringObject =type(strptr ("123"))
    Print zo1
    
    Dim zo2 As ZstringObject = type (strptr(big))
    Print mid(zo2,1,50)

    Dim zo3 As ZstringObject
    zo3 = zo1 + zo2
    

    Print mid(zo3,1,50)
    
    Dim zo4 As ZstringObject
    zo4 = zo3 + zo2
    Print mid(zo4,1,50)
    
    if mid(zo3,1,3)<>"123" then print "error":sleep:exit do
    
    Print mid(zo3,1,50)  

End Scope
loop until x="1500"
print "Done"
sleep
 
fxm's method is more elegant.
And his own method (first post) works perfectly.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Auto-Sized Zstring Object

Post by fxm »

dodicat wrote: Jan 31, 2023 20:48 fxm's method is more elegant.
And his own method (first post) works perfectly.
Thanks.
Post Reply