Announcement

Collapse
No announcement yet.

Dynamic strings in struct..

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

  • Dynamic strings in struct..

    We (urgently) need to know if this will work..

    This seems a solution to make use of dynamic string in a struct.

    As long the VarPtr and StrPtr not go out of scope, PB seems to keep the data present.
    Is this true??

    Now we don't need HeapAlloc.
    We need memory pointers where we can make use of Instr()

    Thanks,

    Code:
     
    #Compile Exe
    #Include "win32api.inc"
     
    Type DynamicString
     
        v As Dword      '// VarPtr for maintenance
        s As String Ptr '// StrPtr to actual data
     
    End Type
     
    '// Custom struct.
    Type MyStruct
     
        MyString    As DynamicString
        OtherData   As Long
     
    End Type
     
    '// Be sure you keep the Varptr's and StrPtr's in scope!
    '// Otherwise PB will destroy your data.
    Global SP() As MyStruct
     
    Function SPTest() As Long
     
        Dim T As String Ptr
     
        T = SP( 1 ).MyString.s
        @T = Time$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$
     
    End Function
     
    Function PbMain
     
        ReDim SP( 1 To 2 )
     
        SP( 1 ).MyString.s  = VarPtr( SP( 1 ).MyString.v )
        SP( 1 )[email protected] = "This is a test"
     
        MsgBox SP( 1 )[email protected]
     
        SPTest
     
        MsgBox SP( 1 )[email protected]
    
    End Function

    ------------------
    [email protected]
    hellobasic

  • #2
    It seemes to work...but I am not sure I understand how the allocation
    takes place. I tried to make a GPF with the following code.

    Regards
    Peter

    Later: Now I understand...very nice!
    (I made a minor change to InitDS)

    Code:
    #compile exe
     
    type DS
        v as dword      '// VarPtr for maintenance
        s as string ptr '// StrPtr to actual data
    end type
      
    sub InitDS( ds as DS)
        ds.s = varptr(ds)
    end sub	
      
    sub PutDS(ds as DS, byval txt as string)
        [email protected] = txt
    end sub
      
    function GetDS(ds as DS) as string
        function = [email protected]
    end function
      
    function pbmain
       
        local s as DS : InitDS s
       
        PutDS s, "This is a test"
      
        msgbox GetDS(s)	
      
        PutDS s, repeat$(5000,"A")
      
        msgbox GetDS(s)	
      
    end function
    ------------------




    [This message has been edited by Peter P Stephensen (edited December 11, 2000).]
    [email protected]
    www.dreammodel.dk

    Comment


    • #3
      Edwin --
      This way looks easier for me
      Code:
         #Compile Exe
         Global DummyString As String
      
         Type MyStruct
            MyString As String Ptr
            OtherData As Long
         End Type
      
         Function PbMain
            Dim SP As MyStruct
            SP.MyString = StrPtr (DummyString) ' Initial (it's necessary)
            
            [email protected] = "This is a test"
            MsgBox [email protected]
         End Function
      ------------------
      E-MAIL: [email protected]

      Comment


      • #4
        Edwin,
        I think you will find that the strptr moves???
        This is the way I do it.

        James

        Code:
        #COMPILE EXE
        #DIM ALL
        #REGISTER  NONE
        #INCLUDE "WIN32API.INC"
        #INCLUDE "OLEAUT32.INC"
        TYPE MYTYPE
          Str1		AS ASCIIZ PTR
          dwData	AS DWORD
          Str2		AS ASCIIZ PTR
        END TYPE
        
        GLOBAL SP() As MYTYPE
         
         
        Function PbMain
          REDIM SP( 1 To 2 )
          SP(1).Str1 = SysAllocStringByteLen("This is a Test",50)
          MsgBox SP(1)[email protected]
          SysReAllocString VARPTR(SP(1).Str1),(Time$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & CHR$(0))      
          MsgBox SP( 1 )[email protected]
          SysFreeString SP(1).Str1
        End Function
        ------------------

        Comment


        • #5
          Cute concept - it is similar to how VB handles dynamic strings in UDT's.

          A few other comments:

          1. The UDT contains a dynamic string pointer, so it should be assigned with VARPTR(dynamic string) instead of STRPTR(dynamic string)

          2. Since the string has no initial content in the (incorrect) above example, assigning text to the target is very likely to cause a GPF since the wrong target address was used to begin with.

          3. If the global string is changed with a normal assignment, the pointer is guaranteed to be void, which means updating the pointer in the UDT whenever the string data is changed with a standard concatenation/assignment. To prove it, try this slight variation of the (incorrect) code and you'll see it fails miserably at the second MSGBOX (since STRPTR was used instead of VARPTR).
          Code:
             #COMPILE EXE
             GLOBAL DummyString AS STRING
          
             TYPE MyStruct
                MyString AS STRING PTR
                OtherData AS LONG
             END TYPE
          
             FUNCTION PBMAIN
                DIM SP AS MyStruct
                SP.MyString = STRPTR(DummyString) ' Initial (it's necessary)
          
                [email protected] = "This is a test"
                MSGBOX [email protected]
          
                DummyString = REPEAT$(20, "<*>")
                MSGBOX [email protected] ' hmmm! We risk a GPF at this point...
          
                MSGBOX DummyString
             END FUNCTION
          The moral is that you have to plan the use of this type of code VERY carefully to ensure you are working with valid memory addresses, and that you start off using the correct target address (VARPTR)

          My $0.02...


          ------------------
          Lance
          PowerBASIC Support
          mailto:[email protected][email protected]</A>
          Lance
          mailto:[email protected]

          Comment


          • #6
            Lance --
            Well, I'm agree about initial value .
            What do you think about this ?
            Code:
               #Compile Exe
               Global DummyString As String
               
               Type MyStruct
                  MyString As String Ptr
                  OtherData As Long
               End Type
            
               Function PbMain
                  Dim SP As MyStruct
                  SP.MyString = VarPtr(DummyString) ' Initial
                  MsgBox Hex$(StrPtr([email protected])), , "Initial location of the string data"
            
                  [email protected] = "This is a test"
                  MsgBox [email protected]
                  
                  MsgBox Hex$(StrPtr([email protected])), , "New location"
                  
               End Function
            ------------------
            E-MAIL: [email protected]

            Comment


            • #7
              Before we get to much alternatives..

              Is there a method without using alloc api's?

              JC, the method you describe is not a string, i need chr$(0) too.

              Semen, we just tried a lot of data, after reading 3 entries a GPF occures.


              ------------------
              [email protected]
              hellobasic

              Comment


              • #8
                Just an idea. Did not have the time to test it...
                Make a DynString-object, defined by the header-file:

                Code:
                #if not %def(%DS_INC)
                %DS_INC = 1
                '------------------------------------------------------
                  
                %NewDS  = 1    ' Constructor
                %KillDS = 2    ' Destructor
                %GetDS  = 3    ' Methods
                %PutDS  = 4
                 
                function DSMainFunction(byval funcID as long, str as string, byval param as long) as long
                 
                    static DSMeromy() as string
                    static count as long
                    static HandleStack() as long
                    static InitFlag as long
                  
                    local hDS as long
                 
                    if InitFlag = 0 then
                        InitFlag = 1
                        redim DSMemory(0)
                        redim HandleStack(0)
                    end if
                  
                    select case funcID
                    case %NewDS     ' Constructor
                        if ubound(HandleStack()) > 0 then
                            hDS = HandleStack(ubound(HandleStack()))
                            redim preserve HandleStack(ubound(HandleStack())-1)				
                        else
                            incr count : hDS = count
                            redim preserve DSMemory(count) 
                        end if
                 
                        DSMemory(hDS) = str
                        function = hDS : exit function
                 
                        case %KillDS     ' Destructor
                            redim preserve HandleStack(ubound(HandleStack())+1)				
                            HandleStack(ubound(HandleStack())) = param
                 
                        ' Methods:
                        case %GetDS
                            function = strptr(DSMemory(param)) : exit function
                 
                        case %PutDS
                            DSMemory(param) = str			
                 
                        end select
                 
                        function = 0
                 
                end function
                
                 
                ' Wrapper functions
                function NewDS(byval txt as string) as long
                    function = DSMainFunction(%NewDS, txt,0)
                end function
                 
                sub KillDS(byval hDS as long) as long
                    DSMainFunction %KillDS, "",hDS
                end sub
                 
                function GetDS(byval hDS as long) as string
                 
                    local pz as asciiz pointer
                  
                    pz = DSMainFunction(%GetDS, txt,hDS)
                 
                    function = @pz
                 
                end function
                 
                sub SetDS(byval hDS as long, byval txt as string)
                    DSMainFunction %PutDS, txt, hDS
                end sub
                 
                #endif ' %DYNSTR_INC
                '------------------------------------------------------
                It should be used this way:
                Code:
                #compile exe
                #include "ds.inc"
                 
                type MyStruct
                    hString as long
                    OtherData as long
                end type
                 
                function pbmain
                  
                    dim SP as MyStruct
                  	
                    SP.hString = NewDS("This is a test")
                  
                    msgbox GetDS(SP.hString)
                   
                    PutDS SP.hString, repeat$(1000,"A")
                   
                    msgbox GetDS(SP.hString)
                  
                end function
                Regards
                Peter


                ------------------
                [email protected]
                www.dreammodel.dk

                Comment


                • #9
                  GPF ?! I don't see GPF here (under ME).
                  1000 times looks enough for me.

                  Code:
                     #Compile Exe
                     
                     Type MyStruct
                        MyString As String Ptr
                        OtherData As Long
                     End Type
                     
                     Function PbMain
                        Dim nStruct As Long
                        nStruct = 100
                        
                        Dim i As Long, j As Long
                        ReDim SP(1 To nStruct) As MyStruct, DummyString(1 To nStruct) As String
                        For i = 1 To nStruct: SP(i).MyString = VarPtr(DummyString(i)): Next ' Initial
                            
                        ' Test
                        For j = 1 To 1000
                           For i = 1 To nStruct
                              SP(i)[email protected] = "Element #" + Str$(i) + " Cross #" + Str$(j)
                          Next
                        Next
                        
                        MsgBox SP(1)[email protected], , SP(nStruct)[email protected]
                        
                     End Function
                  ------------------
                  E-MAIL: [email protected]

                  Comment


                  • #10
                    I had no time to test these suggestions.
                    At the moment i maintain 2 arrays, 1 for dyn. strings only.
                    This was the safest.

                    I also created 2000000 entries, and read them again.
                    Worked fine until..

                    I kept it all in a global array.
                    While reading it from a different procedure the 3th entry crashed!

                    So, try different procedures.
                    I noted that the varptr stored was different and equal to the strptr!!

                    If we can overcome this we have a nice dyn. allocation WITHOUT using heapalloc etc..

                    BTW, note that i need strings (with CHR$(0))

                    ??


                    ------------------
                    ebknop[email protected]
                    hellobasic

                    Comment


                    • #11
                      Well, 2 mln entries, 5 crosses, different procedures.
                      No problems on my PÑ (except time).

                      Probably, your problems are memory/OS relative.
                      Sure, that really is used much more memory than string data requires
                      (additional information about allocated blocks; alignment; fragmentation).


                      Code:
                         #Compile Exe
                      
                         Type MyStruct
                            MyString As String Ptr
                            OtherData As Long
                         End Type
                      
                         %nStruct = 2000000
                         %nCross = 5
                      
                         Global SP() As MyStruct, DummyString() As String
                      
                         Sub Fill
                            Dim i As Long, j As Long
                            ReDim SP(1 To %nStruct), DummyString(1 To %nStruct)
                            For i = 1 To %nStruct: SP(i).MyString = VarPtr(DummyString(i)): Next ' Initial
                      
                            For j = 1 To %nCross
                               For i = 1 To %nStruct
                                  SP(i)[email protected] = "Element #" + Str$(i) + " Cross #" + Str$(j)
                              Next
                            Next
                         End Sub
                      
                         Sub Test
                            Dim i As Long, j As Long
                            j = %nCross
                            For i = 1 To %nStruct
                               If SP(i)[email protected] <> "Element #" + Str$(i) + " Cross #" + Str$(j) Then MsgBox "Oh"
                            Next
                            MsgBox "Ok"
                          End Sub
                      
                         Function PbMain
                            Fill
                            Test
                        End Function

                      ------------------
                      E-MAIL: [email protected]

                      Comment


                      • #12
                        Hi,

                        (RE: Peter P. Stephensen's Code snippet)

                        I fixed up Peter's example so now it will run okay.
                        Does anyone out there foresee any problems with using his
                        approach? I like the idea that it is self-contained in an
                        Include file.


                        #COMPILE EXE
                        #INCLUDE "ds.inc"

                        TYPE MyStruct
                        hString AS LONG
                        OtherData AS LONG
                        END TYPE

                        FUNCTION PBMAIN

                        DIM SP AS MyStruct

                        SP.hString = NewDS("This is a test")
                        MSGBOX GetDS(SP.hString)

                        SetDS SP.hString, REPEAT$(1000,"A")
                        MSGBOX GetDS(SP.hString)


                        END FUNCTION


                        #IF NOT %DEF(%DS_INC)
                        %DS_INC = 1
                        '------------------------------------------------------

                        %NewDS = 1 ' Constructor
                        %KillDS = 2 ' Destructor
                        %GetDS = 3 ' Methods
                        %PutDS = 4

                        FUNCTION DSMainFunction(BYVAL funcID AS LONG, str AS STRING, BYVAL param AS LONG) AS LONG

                        STATIC DSMemory() AS STRING
                        STATIC count AS LONG
                        STATIC HandleStack() AS LONG
                        STATIC InitFlag AS LONG

                        LOCAL hDS AS LONG

                        IF InitFlag = 0 THEN
                        InitFlag = 1
                        REDIM DSMemory(0)
                        REDIM HandleStack(0)
                        END IF

                        SELECT CASE funcID
                        CASE %NewDS ' Constructor
                        IF UBOUND(HandleStack()) > 0 THEN
                        hDS = HandleStack(UBOUND(HandleStack()))
                        REDIM PRESERVE HandleStack(UBOUND(HandleStack())-1)
                        ELSE
                        INCR count
                        hDS = count
                        REDIM PRESERVE DSMemory(count)
                        END IF

                        DSMemory(hDS) = str
                        FUNCTION = hDS
                        EXIT FUNCTION

                        CASE %KillDS ' Destructor
                        REDIM PRESERVE HandleStack(UBOUND(HandleStack())+1)
                        HandleStack(UBOUND(HandleStack())) = param

                        ' Methods:
                        CASE %GetDS
                        FUNCTION = STRPTR(DSMemory(param))
                        EXIT FUNCTION

                        CASE %PutDS
                        DSMemory(param) = str

                        END SELECT

                        FUNCTION = 0

                        END FUNCTION


                        ' Wrapper functions
                        FUNCTION NewDS(BYVAL txt AS STRING) AS LONG
                        FUNCTION = DSMainFunction(%NewDS, txt, 0)
                        END FUNCTION

                        SUB KillDS(BYVAL hDS AS LONG)
                        DSMainFunction %KillDS, "", hDS
                        END SUB

                        FUNCTION GetDS(BYVAL hDS AS LONG) AS STRING

                        LOCAL pz AS ASCIIZ pointer
                        LOCAL txt AS STRING

                        pz = DSMainFunction(%GetDS, txt, hDS)

                        FUNCTION = @pz

                        END FUNCTION

                        SUB SetDS(BYVAL hDS AS LONG, BYVAL txt AS STRING)
                        DSMainFunction %PutDS, txt, hDS
                        END SUB

                        #ENDIF ' %DYNSTR_INC
                        '------------------------------------------------------



                        ------------------
                        Paul Squires
                        [email protected]
                        Paul Squires
                        FireFly Visual Designer (for PowerBASIC Windows 10+)
                        Version 3 now available.
                        http://www.planetsquires.com

                        Comment


                        • #13
                          Edwin,
                          You say you need chr$(0)? Actually the SysAllocStrByteLen is provided for just that
                          purpose. I just showed access using string related functions. See if this might
                          be a bit closer.
                          James
                          Code:
                          #COMPILE EXE
                          #DIM ALL
                          #REGISTER  NONE
                          #INCLUDE "WIN32API.INC"
                          #INCLUDE "OLEAUT32.INC"
                          TYPE MYTYPE
                            Str1		AS ASCIIZ PTR
                            dwData	AS DWORD
                            Str2		AS ASCIIZ PTR
                          END TYPE
                          
                          GLOBAL SP() As MYTYPE
                           
                           
                          Function PbMain
                            LOCAL dwTempPtr	AS DWORD PTR, _
                                  sTemp		AS STRING
                            REDIM SP( 1 To 2 )
                            sTemp = "This is a Test"+CHR$(0,13,10)+"Some more text"
                            SP(1).Str1 = SysAllocStringByteLen((sTemp),LEN(sTemp))
                            'Ok you need chr$(0)'s in your string. Just get the length and use peek$
                            'the length is the dword pointer preceeding the actual ASCZII PTR
                            dwTempPtr = SP(1).Str1
                            DECR dwTempPtr
                            sTemp = PEEK$(SP(1).Str1,@dwTempPtr)
                            MsgBox FORMAT$(LEN(sTemp))
                            SysReAllocString VARPTR(SP(1).Str1),(Time$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & ", " & Date$ & CHR$(0))      
                            MsgBox SP( 1 )[email protected]
                            SysFreeString SP(1).Str1
                          End Function
                          ------------------


                          [This message has been edited by jcfuller (edited December 12, 2000).]

                          Comment


                          • #14
                            Yes, i think so, a year ago i saw the complete buffer from an asciiz.
                            It's simply an offset in the string containing the 0.

                            But unf. now i need to destroy the alloc. data myself.
                            The point is that i can use simple strings (Maybe with as simple wrapper)
                            The procedure i wrote did that until i reread the data in a diff. proc.
                            I didn't the last examples because i'm to busy.
                            At the moment we use 2 arrays wich solves the problem.
                            The Strptr as described above was much faster.

                            If there's a way to 'fool' or help PB to make this a sure working procedure


                            ------------------
                            [email protected]
                            hellobasic

                            Comment

                            Working...
                            X