Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Strings in Types

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin Strings in Types

    I was looking at whether I could get a dynamic string into a type that I could use PB's routines against. Here's the result.

    Essentially you declare a dword somewhere (such as in a type).

    You do nothing, it's initially an empty string.
    If you want to assign it a value you use the result of AllocateString( "my value" )
    When you no longer want the string you call FreeString

    If you want to use PB's string routines you call AquireString. When you're done using the routines you call ReleaseString.

    You can get the length of the string calling StringLen without acquiring the string.

    Could have some bugs in it, but the trivial example seemed to work...

    ManualStrings.bas
    Code:
    #Compile Exe
    #Dim All
     
    #Include "windows.inc"
     
    ' Makes value be the string pointed to by p.  These can only be used as RVAL's.
    'Sub AquireString( ByVal p As Dword, value As String )
     
    ' Releases a string that was aquired
    'Sub ReleaseString( value As String )
     
    ' Returns the length of a string without aquiring it
    'Function StringLen( ByVal p As Dword Ptr ) As Long
     
    'Assigns a value to a string
    'Sub AssignString( p As Dword, value As String )
     
    'Returns a pointer to a newly allocated string
    'Function AllocateString( value As String ) As Dword
     
    'Releases the memory for an Assigned or Allocated string
    'Sub FreeString( p As Dword )
     
    'Copies a string
    'Sub CopyString( src As Dword, dst As Dword )
     
    'Returns a safe PB string, can be safely used as an lval, does not need to be released
    'Function SafeString( src As Dword ) As String
     
     
    Type StringExample
      name As Dword
      other As Dword
    End Type
     
    Function PBMain () As Long
      Local v As StringExample
      v.name = AllocateString( "This is a test" )
     
     
      Local b, a As String
      AquireString v.name, a
      b = a + " for sure"
      ReleaseString a
      AssignString v.name, b
      ? a ' s/b empty
      AquireString v.name, a
      ? a ' s/b "This is a test for sure"
      ReleaseString a
     
      ? Str$( StringLen( v.name ) )
     
      CopyString v.name, v.other
      AquireString v.other, a
      ? a
      ReleaseString a
     
      FreeString( v.name )
      FreeString( v.other )
    End Function
     
     
    Sub AquireString( ByVal p As Dword, value As String )
      value = "" ' Release whatever the string was pointing to
      Poke Dword, VarPtr( value ), p
    End Sub
     
    Sub ReleaseString( value As String )
      Poke Dword, VarPtr( value ), 0
    End Sub
     
    Function StringLen( ByVal p As Dword Ptr ) As Long
      p-=4
      Function = @p
    End Function
     
    Sub AssignString( p As Dword, value As String )
      If p Then FreeString p
      p = AllocateString( value )
    End Sub
     
    Function AllocateString( value As String ) As Dword
      Local p As Dword Ptr
     
      If Len(value)=0 Then
        Function = 0
        Exit Function
      End If
      p = GlobalAlloc( %GMEM_FIXED, Len(value)+5 )
      Memory Copy StrPtr(value), p+4, Len(value)+1
      @p = Len(value)
      Function = p+4
    End Function
     
    Sub FreeString( p As Dword )
      If p=0 Then Exit Sub
      p -= 4
      GlobalFree( p )
      p = 0
    End Sub
     
    Sub CopyString( src As Dword, dst As Dword )
      Local a As String
     
      If src=dst Then Exit Sub ' Self copies are really quick!
      If dst<>0 Then FreeString dst
      AquireString src, a
      dst = AllocateString( a )
      ReleaseString a
    End Sub
     
    Function SafeString( src As Dword ) As String
      Local a, b As String
      AquireString src, a
      b = a
      ReleaseString a
      Function = b
    End Function
    Edit: Note, these strings should only be used as rval, never as lvals. what that means is you can use them, but you can't ask PB to alter them. You can assign to a pb string, alter the pb string, and then assign back. Some examples of where not to use acquired strings are:
    aquiredString += value ' BAD
    Replace "x" with "y" In aquiredString ' BAD

    The basic rules are:
    Any AquiredString must call ReleaseString when you are done with the string variable.
    Any AssignString, AllocateString, CopyString must call FreeString when the string value is no longer needed.

    I added SafeString which returns a native PB string and can be used in any context. It does not need to be released or freed. It's simpler to use at the cost of a string assignment (i.e. copy).
    Last edited by Larry Charlton; 9 Feb 2015, 07:23 AM.
    LarryC
    Website
    Sometimes life's a dream, sometimes it's a scream

  • #2
    Larry,
    You must of been reading my mind.
    After spending some time learning about the amount of
    time it takes to create variables in a function.
    For frequent used function. It seems more practical for speed purposes to use a type and declare the variables that take up a fixed amount of memory such as numbers and fixed length strings in a type structure and pass that type structure as using BYREF in the fuctrion.
    But the dynamic string is tough to do.

    Having other work to do. I had to put off the marathon of reworking my code of reading file lines in using the binary method.
    Where I need to work, at least I feel a need to use dynamic strings, I figured that I would just use BYREF for the single dynamic string.

    Anyway, how about using a dynamic string array, with a type variable.
    Any thoughts on that.
    p purvis

    Comment


    • #3
      Here's a simpler and safer version. It has just three routines.

      To initialize a DWORD string, assign a PB string a value and then swap it with your DWORD.

      To use the string in a DWORD, swap it with a local string, do something, and then swap it back.

      You can get the length of a string. If you're done with the string you can release the string.

      The Demo uses a type with three dword variables, assigns each a value, checks each value. Alters the values, checks the altered value, displays the length of each string, releases all three values, and then verifies they were released ok.

      In general, if you have a local type structure you put strings in, be sure to release the strings before you leave the procedure. Try / Finally is one great way to do that.

      If you allocate memory for a type variable, be sure to release the DWORD strings before you free the memory.

      StructStrings.inc
      Code:
      #Include This Once
       
      ' SwapString p As DWord, value As String
      ' Swaps the string referenced by p with the string referenced by value
      '
      ' StringLen p As DWord
      ' Returns the length of the string p
      '
      ' ReleaseString p As DWord
      ' If p has a string, it safely discards it
       
      Sub SwapString( p As Dword, value As String )
        Local a As Dword
        a = p
        p = Peek( Dword, VarPtr( value ) )
        Poke Dword, VarPtr( value ), a
      End Sub
       
      Function StringLen( ByVal p As Dword Ptr ) As Long
        If p=0 Then Exit Function
        p-=4
        Function = @p
      End Function
       
      Sub ReleaseString( p As Dword )
        Local a As String
        SwapString p, a
      End Sub
      StructStings.bas
      Code:
      #Compile Exe
      #Dim All
       
      #Include "structStrings.inc"
       
      Type MyStrings
        a As Dword
        b As Dword
        c As Dword
      End Type
       
      Function PBMain () As Long
        Local a As String
        Local v As MyStrings
        Local ln As Long
       
        a = "This is string a"
        SwapString v.a, a
       
        a = "This is string b"
        SwapString v.b, a
       
        a = "This is string c"
        SwapString v.c, a
       
        SwapString v.a, a
        ? "a='" + a + "' " + IIf$(a="This is string a", "OK", "FAIL" )
        SwapString v.a, a
       
        SwapString v.b, a
        ? "b='" + a + "' " + IIf$(a="This is string b", "OK", "FAIL" )
        SwapString v.b, a
       
        SwapString v.c, a
        ? "c='" + a + "' " + IIf$(a="This is string c", "OK", "FAIL" )
        SwapString v.c, a
       
        SwapString v.a, a
        a += " and only string a"
        SwapString v.a, a
       
        SwapString v.b, a
        a += " and only string b"
        SwapString v.b, a
       
        SwapString v.c, a
        a += " and only string c"
        SwapString v.c, a
       
        SwapString v.a, a
        ? "a='" + a + "' " + IIf$(a="This is string a and only string a", "OK", "FAIL" )
        SwapString v.a, a
       
        SwapString v.b, a
        ? "b='" + a + "' " + IIf$(a="This is string b and only string b", "OK", "FAIL" )
        SwapString v.b, a
       
        SwapString v.c, a
        ? "c='" + a + "' " + IIf$(a="This is string c and only string c", "OK", "FAIL" )
        SwapString v.c, a
       
        ? "StringLen(v.a)="+Str$( StringLen(v.a) )
        ? "StringLen(v.b)="+Str$( StringLen(v.b) )
        ? "StringLen(v.c)="+Str$( StringLen(v.c) )
       
        ReleaseString v.a
        ReleaseString v.b
        ReleaseString v.c
       
        ? "a was released " + IIf$( v.a=0, "OK", "FAIL")
        ? "b was released " + IIf$( v.b=0, "OK", "FAIL")
        ? "c was released " + IIf$( v.c=0, "OK", "FAIL")
        ? "Done"
      End Function
      LarryC
      Website
      Sometimes life's a dream, sometimes it's a scream

      Comment


      • #4
        Here's an example of a dynamic stack using DWord strings. You push values on the stack and then pop them off. Note that it's important if you allocate memory to ensure that all DWord values that will be used as strings start out as 0. GPTR for global alloc does that.

        When popping I just swapped values into the string they were destined for. I did this because I was just going to throw away the stack entry once done but I still had to release them. That's because the strings coming in could (and often did) have values before I assigned them values.

        StructStringStack.bas
        Code:
        #Compile Exe
        #Dim All
         
        #Include "windows.inc"
        #Include "structStrings.inc"
         
        Type MyStack
          shape As Dword
          texture As Dword
          size As Single
          nxt As MyStack Ptr
        End Type
         
        Sub Push( list As Dword, shape As String, texture As String, sz As Single )
          Local a As String
          Local p As MyStack Ptr
          p = GlobalAlloc( %GPTR, SizeOf(MyStack) ) ' The DWords must be 0 initially!
          a = shape:    SwapString @p.shape, a
          a = texture:  SwapString @p.texture, a
          @p.size = sz
          @p.nxt = list
          list = p
        End Sub
         
        Function Pop( list As Dword, shape As String, texture As String, sz As Single ) As Long
          Local p As MyStack Ptr
          If list = 0 Then Exit Function
          p = list
          list = @p.nxt
          SwapString @p.shape, shape
          SwapString @p.texture, texture
          sz = @p.size
          ReleaseString @p.shape
          ReleaseString @p.texture
          GlobalFree( p )
          Function = -1
        End Function
         
         
        Function PBMain () As Long
          Local stack As MyStack Ptr
          Local shape, texture As String
          Local sz As Single
         
          Push stack, "round", "smooth", 2.5!
          Push stack, "square", "rough", 2.8!
          Push stack, "round", "rough", 3.1!
          Push stack, "square", "smooth", 1.8!
         
          While pop( stack, shape, texture, sz )
            ? "Shape="+shape+", texture="+texture+", size="+Str$(sz)
          Wend
          ? "Done"
        End Function
        LarryC
        Website
        Sometimes life's a dream, sometimes it's a scream

        Comment


        • #5
          Paul,

          Posted an example of storing an array in a type variable. It's a bit larger... 116 bytes, I wouldn't want to drag it out or put it away a lot.
          LarryC
          Website
          Sometimes life's a dream, sometimes it's a scream

          Comment


          • #6
            Alternate struct string uses

            Based on Michael's suggestion, here's some additional ways structure strings can be used.

            From a "I don't like poking and peeking" perspective, string pointers provide an alternative to using swap. Use what you like. String pointers will be faster for short uses, the swap routine should be faster if you use the string a lot between swaps.

            There's also three examples of passing the string to sub routines.

            AlternateUse.bas
            Code:
            #Compile Exe
            #Dim All
             
            #Include "structStrings.inc"
             
            Type MyType
              name As Dword
              pName As String Ptr
            End Type
             
            Function PBMain () As Long
              Local s As MyType
             
              s.pName = VarPtr( s.Name ) ' Initialization
             
              s.@pName = "Testing"
             
              s.@pName += " struct strings"
             
              ShowName ByVal s.pName
             
              ShowName s.@pName
             
              ShowName ByVal VarPtr( s.name )
             
              Call ReleaseString( s.name )
            End Function
             
            Sub ShowName( aName As String )
              ? aName
            End Sub
            LarryC
            Website
            Sometimes life's a dream, sometimes it's a scream

            Comment

            Working...
            X