Help changing string value "byref" in procedure

Just starting out? Need help? Post your questions and find answers here.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Help changing string value "byref" in procedure

Post by mesozorn »

I found this in another thread, and it seems to work:

Code: Select all

Procedure change(*u.String)  
*u\S ="vladimir was here and so was I!"
EndProcedure

u.String\S="bernard"
a$="Hello!"
Debug u\S
change(@u)
Debug u\S
Debug a$
My question is, how can I accomplish this same thing, if I do *NOT* want to use a "structure" as the original string variable, outside the procedure. I just want it to be a normal s.s type string variable, not a s.string structure, where I have to use s\s every time to refer to it.

What is the easiest way to do this? Thanks...
Little John
Addict
Addict
Posts: 4777
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Help changing string value "byref" in procedur

Post by Little John »

mesozorn wrote:I found this in another thread, and it seems to work:

Code: Select all

Procedure change(*u.String)  
*u\S ="vladimir was here and so was I!"
EndProcedure

u.String\S="bernard"
a$="Hello!"
Debug u\S
change(@u)
Debug u\S
Debug a$
Yes, that works.
mesozorn wrote:My question is, how can I accomplish this same thing, if I do *NOT* want to use a "structure" as the original string variable, outside the procedure. I just want it to be a normal s.s type string variable, not a s.string structure, where I have to use s\s every time to refer to it.

What is the easiest way to do this? Thanks...
I don't think that there is a way to do this.

Regards, Little John
jamba
Enthusiast
Enthusiast
Posts: 144
Joined: Fri Jan 15, 2010 2:03 pm
Location: Triad, NC
Contact:

Re: Help changing string value "byref" in procedure

Post by jamba »

I was searching for an existing thread on this, after seeing posts like this:
http://www.purebasic.fr/english/viewtop ... 69#p325469

this works, but is there a smarter way to go about it?

Code: Select all

Procedure.i ByRefString(*buffer, newValue.s)
  Protected.i bufLength = MemoryStringLength(*buffer)
  If Len(newValue) > bufLength
    PokeS(*buffer, Left(newValue,bufLength-1) + "~") ;truncated value
    ProcedureReturn 0
  EndIf
  
  PokeS(*buffer, newValue, bufLength)
  
  ProcedureReturn 1
EndProcedure

Macro m_ByRefString(txtptr,newValue) ;could also use a macro for this
  If Len(newValue) <= MemoryStringLength(txtptr)
    PokeS(txtptr, newValue)
  Else
    PokeS(txtptr, "~") ;truncated value
  EndIf
EndMacro

Procedure.s testProc2(*txt)
  ;this is just an internal procedure that needs a string byref (in addition to the procedure return)
  ;this is when the checking would be recommended.
  
  If Not ByRefString(*txt,"internal DLL changed")
    ProcedureReturn "error"
  EndIf
  
  ProcedureReturn "changed"

EndProcedure

Procedure.s testProc3(*txt)
  ;this is just an internal procedure that needs a string byref (in addition to the procedure return)
  ;this is when the checking would be recommended.
  
  m_ByRefString(*txt,"internal DLL changed")
  
  ProcedureReturn "changed"

EndProcedure


Define s.s

s=Space(20)
Debug testProc2(@s)
Debug s

s=Space(20)
Debug testProc3(@s)
Debug s
-Jon

Fedora user
But I work with Win7
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Help changing string value "byref" in procedure

Post by charvista »

Mesozorn,
Here a simplification for all types variables that should passed "ByRef" to procedures (here only a string and an integer are used):

Code: Select all

Procedure zByRef(*A.string, *B.integer) ; .double for double, .long for long, etc.
    Debug "Inside: "+PeekS(*A)
    Debug PeekI(*B)
    cc.s="gerac"
    PokeS(*A,Left(PeekS(*A),3)+cc.s) ; change it now
    PokeI(*B,PeekI(*B)*10+2)
    Debug "Still Inside: "+PeekS(*A)
    Debug PeekI(*B)
EndProcedure 

V.s="Bernard"
C.i=938
Debug "Before: "+V
Debug C
zByRef(@V,@C) 
Debug "After: "+V
Debug C
Your first post with 'Vladimir' was my post a long time ago, when I had the ByRef problem and at this time I did not understand the pointers yet!
This is probably the unique and easiest way to pass variables "ByRef" as used in other Basics.
Hope this helps you.
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Help changing string value "byref" in procedure

Post by srod »

Charvista, that code is flawed since you can easily write past the end of the original character buffer and cause all sorts of problems. It's okay if your procedure writes a new string which is no longer than the original, but attempt to write a longer one and anything might happen!

E.g. the following produces nonsense on my system. It may or may not follow suit on yours, but even if it doesn't, there still remains this problem :

Code: Select all

Procedure zByRef(*A.STRING) ; .double for double, .long for long, etc.
    Debug "Inside: "+PeekS(*A)
    PokeS(*A,"ABCDEFGHIJKLMNOPQRSTUVWXYZ") ; change it now
    Debug PeekS(*A)
    Debug PeekS(*A)
EndProcedure 

V.s="Bernard"
zByRef(@V) 
Debug "After: "+V
The problem is that @V is passing the address of the character buffer and not the address of the string variable itself.

Another way around the problem is to use a string array containing 1 element in place of a string variable :

Code: Select all

Procedure zByRef(*ptr.STRING)
  Debug "Before = " + *ptr\s
  *ptr\s= "Hello matey boy!"
  Debug "Inside proc = " + *ptr\s
EndProcedure

Dim a$(0)
a$(0) = "Hello!"

zByRef(a$())

Debug "After = " + a$(0)
I may look like a mule, but I'm not a complete ass.
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Help changing string value "byref" in procedure

Post by charvista »

The problem is that @V is passing the address of the character buffer and not the address of the string variable itself.
Damn! You are completely right Srod! Indeed I also get nonsense results! (PeekS(*A) is getting longer and longer)
Until now I did not encounter any problem, but soon or later I would, and then should have difficulties to find out why. So, thank you for the explanation. And I thought I found a parallel way to use strings "ByRef" along with the integers, double, etc...
This leads me to a general question to the PB-Team: if using memory is so much faster than standard variables, why are the standard variables not internal memory allocations? Which are automatically re-allocated when modified. To the end-user it would be a lot easier!
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Help changing string value "byref" in procedure

Post by skywalk »

Yes, this topic is especially frustrating to a VB6 user. (ByRef means ByRef for ALL variable types! Strings, doubles, arrays, structures,etc.)
So, thanks to srod and demivec and jamba and others for finally drilling it into my thick skull.

It finally clicked with srod's "Dim A$(0)". This string element reallocates on the fly in the ByRef structured pointer case. Unlike a simple string variable, which causes all sorts of trouble if written beyond its original extents. So, my question for the PB-Team, is why can't simple string variables behave the same as 1-dimensional arrays of 1 element "behind the scenes". Emphasis on the Basic in PureBasic.

All bluster aside, I scanned through many thousands of lines of my code and there are actually only a few procedures that are manipulating strings in a ByRef fashion.
So much easier to design them out than to jump through the many hoops required here. :wink:

1. Use PeekS(*txt) / PokeS(*txt, "change to") inside Procedure(*txt)
Seems the simplest choice, as long as you do NOT write larger strings into unknown memory.

2. Use Global string variables
Works, but not my preference. Can get hard to keep track of status of variable.

3. Define string variables as Structures(S.string\s = "s") or 1 dimensional String array elements(dim S.s(0)) for later referencing inside procedures.
This works, but not elegant and lots to type. :(

Code: Select all

EnableExplicit
Define.s S
Define S2.String
Structure ST
  x.i
  s.s
  y.i
EndStructure
Global ST.ST

Procedure ByRef4StrVar(*txt) ; Pointer to string variable
  ; Use this method for String variables and 
  ; structures containing string variables.
  ; Clips string to original length
  Protected.i inStrLen = MemoryStringLength(*txt)
  Protected.s S
  S = "Changed --> " + PeekS(*txt)
  If Len(S) > inStrLen
    PokeS(*txt, Left(S,inStrLen-1) + "~")   ; truncated value
  Else
    PokeS(*txt, S, inStrLen)  
  EndIf
EndProcedure

Procedure ByRef4StrArr(*ptr.String)
  ; Use this method to dynamically change String Array elements
  *ptr\s= "A" + Space(128) + "Z"
  *ptr\s = ReplaceString(*ptr\s, " ", "~")
EndProcedure

Procedure NestedByRef(*txt) 
  ByRef4StrVar(*txt)
EndProcedure

Debug "Using ByRef4StrVar:"
S = "S"
Debug "S Before    --> " + S
ByRef4StrVar(@S)
Debug "S After     --> " + S
Debug "S After     --> " + S
Debug "S After     --> " + S

Debug ""
Debug "Using ByRef4StrVar:"
st\s = "err"
Debug "ST\S Before --> " + st\s
ByRef4StrVar(@st\s)
Debug "ST\S After  --> " + st\s

Debug ""
Debug "Using ByRef4StrArr:"
S2\s = "S2"
Debug "S2\s Before --> " + S2\s
ByRef4StrArr(@S2)
Debug "S2\s After  --> " + S2\s

Debug ""
Debug "Using ByRef4StrArr:"
Dim a$(0)
a$(0) = "A"
Debug "A$(0) Before --> " + A$(0)
ByRef4StrArr(@a$())
Debug "A$(0) After  --> " + A$(0)
Debug Len(a$(0))

Debug ""
Debug "Using Nested ByRef:"
S = "S"
Debug "S Before --> " + S
NestedByRef(@S)
S = "SS"
NestedByRef(@S)
Debug "S After  --> " + S
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Post Reply