Passing strings to procedures "By Ref"(theoretical

Just starting out? Need help? Post your questions and find answers here.
iNX
User
User
Posts: 27
Joined: Wed Jan 24, 2007 12:15 pm

Passing strings to procedures "By Ref"(theoretical

Post by iNX »

Let's suppose i want to pass a string to a procedure which modifies the string contents.
If i pass a structure of type .string i can do everything with the string defined inside the structure.

For example:

Code: Select all

Procedure ModString(*sa.String)
 *sa\s = "123456789"
EndProcedure

sp.String
sp\s="12" ; this is optional

ModString(@sp)

Debug sp\s ; returns "123456789"
So i can assign a greater number of characters and memory allocation will be handled automatically.

But if start with a simple string (sp$="12"), without defining a structure of type .string, is it correct to say that i can't achieve something similar to the example above, without handling manually memory allocation/deallocation? (or maybe i can't at all)
Obviously i'm not taking into consideration of returning the string from the Procedure (ModString().s) or using globals.

Code: Select all

Procedure ModString(???)
  ???
EndProcedure

sp$="12"
ModString(???)
Debug sp$

srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

You can certainly achieve the same results using the PB memory commands, but you will most probably have to take care of freeing the resulting memory block yourself. On the other hand, your first solution will enable Purebasic to handle the garbage collection automatically.

I know which method I would use! :)
I may look like a mule, but I'm not a complete ass.
iNX
User
User
Posts: 27
Joined: Wed Jan 24, 2007 12:15 pm

Post by iNX »

Thank you srod, you gave me confirmation of what i was thinking.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Code: Select all

Procedure ModString(*sa.S)
 *sa = "123456789"
EndProcedure

sp.s
sp = "12"

ModString(@sp)

Debug sp
But be careful, the *.s type is a bit flaky since PB 4.

Edit: By the way, there is no "by ref" in PB, everything is by value (by value of pointer).
iNX
User
User
Posts: 27
Joined: Wed Jan 24, 2007 12:15 pm

Post by iNX »

@Trond

that's interesting. It's the most obvious thing, but it works flawlessly!
I think i didn't find this solution because i got confused by many topics about pointers and strings, and even by the PB documentation itself.
For example, in the PB help, i read: "As a consequence the type of a pointer depends of the CPU address mode, (‘long’ on 32 bits CPU and ‘quad’ on 64 bits one for example), so a pointer is a variable of type pointer.
It results from this that assigning a type to a pointer (*Pointer.l , *Pointer.b …) makes no sense."

But from what you showed to me i would say that .s assigned to a pointer has much sense, because it allows the pointer to be dereferenced automatically. And it seems to me that this only happens with pointers of type .s, making possible to modify the contents of the string the pointer is referencing, with further memory allocation (if needed) automatically handled .

For example, if i have a *p.l as a procedure argument, and in the procedure i use *p=10 i think that i'm not writing 10 to the variable whose address i'm passing through p, but i think that i'll write 10 to the pointer itself, screwing things up, since 10 is obviously not a valid memory address.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Yes, it only makes sense with *.s, no other standard types.
iNX
User
User
Posts: 27
Joined: Wed Jan 24, 2007 12:15 pm

Post by iNX »

Some more things:

Code: Select all

Procedure ModString(*sa.S)
sb.s
sb="abcd"
 
*sa = "123456789"
*sa = @sb ; "can't write a numerical value into a string variable" error


EndProcedure

sp.s
sp = "12"

ModString(@sp) ; here i'm assigning an address to *sa.s
               ; but assigning directly an address  to *sa ( *sa=@anystring) is not allowed

Debug sp
So it's possible to assign a -pointer to a string- (@anystring) to a *stringpointer.s through a function argument, but non directly (*stringpointer=@anystring).
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

I would say that in the declaration:

Code: Select all

*ptr.s
because .s is not regarded as a structure in Purebasic, then *ptr is nor regarded, in this case, as a pointer (in the PB sense), but is regarded as a string variable instead.

The reason your code 'works' in a procedure declaration is undoubtedly because string parameters are handled by placing addresses on the stack anyhow.

Indeed, Fred might well consider this to be a bug - I don't know. It's interesting though.
I may look like a mule, but I'm not a complete ass.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

iNX, yes, there are some inconsistencies that shouldn't be there. Fred said he was looking (presumably for the cause).

srod, it should work.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
iNX
User
User
Posts: 27
Joined: Wed Jan 24, 2007 12:15 pm

Post by iNX »

@ Psychophanta

Thank you for the links. I read them all, and i'll start quoting from the last one you posted, the one you started.
Code:
*var2.b=AllocateMemory(100); *var2 is a pointer (i.e. in a 32 bit architecture *var2 is a long -32 bit- ), which is suposed to point to a byte variable.

*var3.s=AllocateMemory(100); *var3 is a pointer (i.e. in a 32 bit architecture *var3 is a long -32 bit- ), which is suposed to point to a string variable.
As you can see I can not define a pointer to a string and assign it a pointer value Surprised
Maybe you had already discovered it by yourself, however as you can read from Trond's posts above and from my answers to it, there's actually one way to assign a pointer to a string to a *var3.s object: utilizing *var3.s as a parameter of a procedure.

I was thinking that passing strings this way was safe for modifying these strings within procedures, since PB would have handled all needed memory allocation in case that i had written to the string a number of characters greater than the size it had before the procedure call.
But i've just discovered this:

Code: Select all

Procedure ModString(*sa.s)
*sa="21" ; this is ok
;*sa="2434567892434567892434" ; this screws things up
EndProcedure

sp.s
st.s
sp="12"
st="16"

ModString(@sp) 
Debug sp

At this point, the use of *stringpointer="something" imho makes even less sense, and maybe should be prohibited, so that *stringpointers.s will be numeric and people will have to use Pokes to use them for writing.
Maybe this way none will think what i was thinking (wrongly) about automatic memory allocation.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

To my mind, PB has it right in that pointers, in terms of de-referencing operations at least, are intended really for structures. It keeps things very 'clean' and simple.

Incidentally, the following is a kind of workaround in that you don't need to define a structure of type STRING in order to call the procedure and avoids Peek's and Poke's!

Code: Select all

Procedure ModString(a) 
  *x.string=@a
  *x\s="2434567892434567892434" ; this screws things up no longer!
EndProcedure 

x.s 
x="ab" 

ModString(@x) 
Debug x
I may look like a mule, but I'm not a complete ass.
iNX
User
User
Posts: 27
Joined: Wed Jan 24, 2007 12:15 pm

Post by iNX »

srod wrote:To my mind, PB has it right in that pointers, in terms of de-referencing operations at least, are intended really for structures. It keeps things very 'clean' and simple.
My opinion is that pointers are really useful and indispensable. I'm only wondering about the need for a *stringpointer.s="pppppp" that acts differently from all other pointers, acts differently if used in a procedure parameter or in a standalone assignment, and may also lead to errors because it doesn't handle memory allocation (or something like that).
Incidentally, the following is a kind of workaround in that you don't need to define a structure of type STRING in order to call the procedure and avoids Peek's and Poke's!

Code: Select all

Procedure ModString(a) 
  *x.string=@a
  *x\s="2434567892434567892434" ; this screws things up no longer!
EndProcedure 

x.s 
x="ab" 

ModString(@x) 
Debug x
Unfortunately the above procedure doesn't work too. :(
You can see it if you add another variable. Try this way:

Code: Select all

Procedure ModString(a)
  *x.string=@a
  *x\s="2434567892434567892434767767677676" ; this screws things up
EndProcedure

x.s
y.s
x="ab"
y="cd"
ModString(@x)
Debug x
I think that this is happening because what you're doing here is a sort of manually recreating a structure containing a s.s, using a pointer to pointer. But probably the compiler doesn't see it as a real structure, so it doesn't handle memory properly.

In the end, it would seem that the only way for passing a string to a procedure and being able to modify it freely from inside the procedure, was utilizing a structure of type .string instead of a simple string, and passing a pointer to that structure like in the first code snippet of this thread. ( not considering inline assembly and/or manual memory allocation/garbage collection)

edit:

Here we can see how memory is handled properly, with a reallocation of the x\s buffer.

Code: Select all

Procedure ModString(*a.string)

  *a\s="2434567892434567892434767767677676" 
EndProcedure

x.string
y.string
x\s="ab"
y\s="cd"
Debug @x\s
ModString(@x)
Debug x\s
Debug @x\s ; here we can see that the buffer of x\s has been reallocated since its address has changed.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Interesting. Yes I agree that your last code is fine and is generally the way I proceed anyhow.

I've had a look at the asm generated by my code and am not sure where the problem is. I can only guess that memory is being freed before time somewhere along the line! Still, the whole business of using pointers in this way doesn't sit right with me anyhow! :)
I may look like a mule, but I'm not a complete ass.
iNX
User
User
Posts: 27
Joined: Wed Jan 24, 2007 12:15 pm

Post by iNX »

Ok, i investigated further, looking at the asm sources and i should have found out where the problem lies.
A bigger problem will be being able to explain it :mrgreen:

Code: Select all

Procedure ModString(a)
  *b.string=@a
  *b\s="2434567892434567892434767767677676" ; At this point, the internal function that handles string assignments (SYS_FastAllocateStringFree) receives the real pointer to the buffer of string x, but we could say that this pointer is in a bad location (ie: it is on the stack). So what happens next?  SYS_Fast.. frees the buffer of string x because it's too small, allocates a new buffer, copies the new string value into the new buffer and puts the address of this new buffer on the stack. So the new buffer it's not "associated" with the real string x (ie: put in v_x), but it's "associated" with the fake b.string structure (that in this case is just a pointer on the stack) and gets lost.

If y is not declared, then the new allocated buffer will remain at the same memory position and the problem won't show up.

x.s
y.s
x="ab"
y="cd"
ModString(@x)
Debug x
Let me know if If i have been unclear and i'll try to clarify ;)
Post Reply