Page 1 of 2

How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 1:16 am
by Marlin
Using this very nice explanation from
http://www.purebasic.fr/english/viewtop ... 58#p291358
to get the terms clarified:
Trond wrote:What is *This.String? Is it a pointer? Yes.
Is it a pointer to string data? No!
Is it a pointer to a string variable? Yes!
What does @String do? Receive a pointer to the string variable? No!
@String (or @"String") receives a pointer to the string data.

*This.String is a pointer to a structure which contains a single string variable.
Now I would like to have a way to get the address of a normal string variable
like mystring.s.

I can get the address of a string variable inside a structure,
like this code sample demonstrates:

Code: Select all

EnableExplicit

Structure val_struct
  a.i
  b.i
  c.s
  d.i
EndStructure

Global structVar.val_struct
Global *String.String

Global stringVar.s = "kat"

*String = @structVar + OffsetOf(val_struct\c)  ; <--- get the adress of structVar\c

structVar\c = "hxsesi"
Debug *String\s
*String\s = "looooooooooooooooooooong"
Debug structVar\c
As you should be able to see with that code,
structVar\c and *String\s are "the same".

Is there any way to get the address of f.e. stringVar.s?

([The address of] a new pointer to the character data of the string
will of course not suffice, as it gets invalid
once the character data needs to be relocated!)


---------------------------------------------------
Edit for future visitors ;-):

I did find ways to "obtain the address of" string variables,
but none of them seems to be more usefull for me
than to use string structures (like stsMyStringStruct.String)
instead of string variables (like strMyString.s) in the first place
if you will want to use the address of the variable at some point
(like when you want to pass the string ByRef).

Nevertheless I'll give an example, in case that can be usefull
to someone:

Code: Select all

EnableExplicit

; This will copy the address of the variable var into the pointer ptr:
Macro AddrToPtr(var, ptr)
  EnableASM
  LEA eax, var
  MOV ptr, eax
  DisableASM
EndMacro

Define strX.s
Define *ptrstrX.String

strX = "dog"

Debug strX     ; dog

AddrToPtr(strX, *ptrstrX)

*ptrstrX\s + "s run around a lot."

Debug strX     ; dogs run around a log.

Remember, variables become invalid outside of their scope
and so do pointers to them!

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 1:32 am
by ts-soft
I hope i have understand the question :wink:

Code: Select all

EnableExplicit

Structure val_struct
  a.i
  b.i
  c.s
  d.i
EndStructure

Global structVar.val_struct
Global *String.String

Global stringVar.s = "kat"

*String = @structVar + OffsetOf(val_struct\c)  ; <--- get the adress of structVar\c

structVar\c = "hxsesi"
Debug *String\s
*String\s = "looooooooooooooooooooong"
Debug structVar\c

Define pointer = PeekI(structVar + 2 * SizeOf(Integer))
Debug PeekS(pointer)

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 1:59 am
by Marlin
ts-soft wrote:I hope i have understand the question :wink:
I'm afraid, it does not seem so.

You made another way to access the string character data of structVar\c,
where pointer only does have a temporary validity!

Code: Select all

EnableExplicit

Structure val_struct
  a.i
  b.i
  c.s
  d.i
EndStructure

Global structVar.val_struct
Global *String.String

Global stringVar.s = "kat"

*String = @structVar + OffsetOf(val_struct\c)  ; <--- get the adress of structVar\c

structVar\c = "hxsesi"
Debug *String\s
*String\s = "looooooooooooooooooooong"
Debug structVar\c

; extended by TS:
Define pointer = PeekI(structVar + 2 * SizeOf(Integer))
Debug PeekS(pointer)

; extended by Marlin:
stringVar = "moooooooooooooooooooooooooooooore"
*String\s = "looooooooooooooooooooonglooooooooooooooooooooong"
Debug PeekS(pointer)
Debug structVar\c
On my system, the last output of "Debug PeekS(pointer)" is "",
while the last output of Debug structVar\c is "looooooooooooooooooooonglooooooooooooooooooooong".

What I want is the address of stringVar.s.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 2:59 am
by IdeasVacuum
Well, you can impose the address:

Code: Select all

 ;Compiled non-unicode

        sStringVar.s = "MyStringVarIsHere"

        *ptrStringVar = AllocateMemory((Len(sStringVar) + 1))
        PokeS(*ptrStringVar,sStringVar,(Len(sStringVar) + 1),#PB_Ascii)

        Debug PeekS(*ptrStringVar)
        FreeMemory(*ptrStringVar)

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 6:30 am
by Demivec
@Marlin: What are you trying to do?

If you want to write to the string, do it via a pointer to the structure. You can read or write the string's contents with either a structure pointer or the string pointer you created.

You cannot write to a string variable which is not in a structure via a pointer because you can only get the address of the string's contents, not the string structure (i.e. @stringvar.s <> @stringStruct.String, @struct\string = @stringStruct.String).

Code: Select all

Structure val_struct
  a.i
  b.i
  c.s
  d.i
EndStructure

Global structVar.val_struct
Global *String.String


;-original value in structure val_struct\c
structVar\c = "hxsesi"     
;existing value read via structure
Debug structVar\c

;get the adress of structVar\c
*String = @structVar + OffsetOf(val_struct\c) 
;orinal value read via pointer *string
Debug *String\s

;-new value assigned via structure
structVar\c = "looooooooooooooooooooong"
;new value read via structure
Debug structVar\c
;new value read via pointer
Debug *String\s

;-new value assigned via pointers
*String\s = "muchloooooooooooooooooooooooooooooooooooooonger"

;new value read via structure
Debug structVar\c
;new value read via pointer
Debug *String\s
@Edit: updated code to be more complete and descriptive. Added additional explanation at top of post.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 1:04 pm
by Trond
Now I would like to have a way to get the address of a normal string variable
like mystring.s.
You can't and you shouldn't.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 2:39 pm
by Marlin
Trond wrote:
Marlin wrote:Now I would like to have a way to get the address of a normal string variable
like mystring.s.
You can't and you shouldn't.
I do not see any real reason, why it has to be that way.

Yes, it does look that way in PB,
except when I use String structures for using strings,
but it could be different, as soon as there was a way
to get that address.

That could look similar like:

Code: Select all

?@mystring.s
or even more descriptive:

Code: Select all

AddressOf(mystring.s)
Then it would be easy to pass references of any string variables as parameters ...

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 3:32 pm
by Marlin
I found another hack to get the address of a local string variable
with the help of a neighboring local variable, I can get the address of:

Code: Select all

EnableExplicit

Global *stsX.String

Procedure SelVar()
  ; local variables reside on the stack
  ; those have addresses expressed in negative numbers (in my case)
  Protected intDummy.i  ; this one I can get the address from
  Protected strX.s
  Protected strPlaceholder.s

  Debug @strX       ; 0  -> No string data allocated yet.
  strX = "first content"
  Debug @strX       ; <some value>  -> this is where the string data are now
  strPlaceholder = "let there be other string data behind strX"
    ; this is meant as a barrier, forcing the string data of strX to be relocated
    ; when strX gets (much) longer (there can be a few extra bytes in reserve)
  
  *stsX = @intDummy + SizeOf(intDummy) ; representing the size of general stack variables
  
  Debug *stsX\s     ; "first content"  -> I got it. :-)
  Debug @strX       ; <some value>  -> string data still where they were
  *stsX\s = "some other, longer content"
  Debug @strX       ; <some other value>  -> string data of strX had to be relocated
  Debug strX        ; "some other, longer content"  -> got the address of the variable,
                       ; not just that of where the string data were
  
EndProcedure


SelVar()

; *stsX is now INVALID, as strX has been removed!

This works on my linux system.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 5:03 pm
by IdeasVacuum
....seems pointless to hack like that :mrgreen:

The regular PB string vars are managed for you, why do you need to read them from memory when the value is already known? If it is because you need to share the string between executables or libs, that's when you need to take control, allocate the memory, Poke the string.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 5:31 pm
by Demivec
@Marlin: What are you trying to accomplish?

In your code sample you use a global variable to point to local strings. That is nonsensical in a of itself, even for hack. :wink: Since the variable is going to hold local values make it local.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 6:08 pm
by Marlin
According to the help,
if you want to return a string
from a library, you need to put it into a global variable in the library.

If this library function is called from different threads,
those might interfere with each other,
all sharing the same global variable.

A mutex can only be used when the same mutex is available
everywhere, where the object it applies to is used.

Besides, I do not need the mutex locked, until the passing of the return value
is began ...
the calling procedure would need to copy the string data somewhere
before another thread could be permitted to do the same...

Unnecessary copying of string data costs resources that could be saved...
(This would only be useable, if the threading problem was solved for that anyways!)

I could use String structures as out parameters,
but then I would require to have all string variables,
I would want to use with that, changed to a String structure and accessed accordingly...

How nice and simple that all could be, if I only could get:

Code: Select all

AddressOf(string.s)

Code: Select all

; call to get result into myStringVariable.s
Libprocedure(inparameters1, inparameter2, ..., AddressOf(myStringVariable))

Code: Select all

; example Libprocedure:
...
ProcedureCDLL Libprocedure(inparameter1.type1, inparameter2.type2, ..., OutString.String)
  ...
  OutString\s = resultstring  ; or whatever
  ProcedureReturn
EndProcedure
...
As well you would be able to pass any stringvariable by reference this way.
(Like in many other programming languages)

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 6:52 pm
by kandl
i dont know what you are trying to do with that but this is how i pass string to anther thread

Code: Select all

Procedure.i StrCpy(string$)
  ProcedureReturn DuplicateMemory(@string$, (Len(string$) + 1) * SizeOf(CHAR))
EndProcedure

; main program

; starting a new thread
Thread(@func(), StrCpy("what you want to pass to the new thread"))

; passing a string parameter the an existing thread
SendMessage(hwnd, msg, StrCpy("string param1"), StrCpy("string param2"))

just remember to free the string memory in your thread when it receives the message

if you're passing the string in the current thread there's no need to duplicate the memory because the string will not be altered while the function is executing

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 7:58 pm
by Demivec
@Marlin: Thanks for your description and explanation. I haven't needed to write code for DLL's or anything similar yet so please excuse me if I misunderstand.

Would a solution like the following work:
  • Have the library procedure 'Libprocedure()', return a pointer to a string or string structure that has it's memory allocated by the Libprocedure(). After the calling code has copied the string data it then calls another library procedure 'LibFreeString()' that frees the memory that was previously allocated.

Doing things this way would allow a string value to be returned with only any extra call to the library. It would get around mutex's needed for reusing global variables. It would all multi-threaded use of the library (according to my understanding).

Code: Select all

;example library procedures

ProcedureCDLL.i Libprocedure(inparameter1.s, inparameter2.s)
  Protected *outString.String = AllocateMemory(SizeOf(String))
  If *outString
    ;sample string results
    *outString\s = inparameter1 + inparameter2
  EndIf
  
  ProcedureReturn *outString
EndProcedure

ProcedureCDLL LibFreeString(*String.String)
  ClearStructure(*String, string)
  FreeMemory(*String)
EndProcedure

;example calling code

;after library has been opened...
Define *resultString.String, myString_1.s, myString_2.s, myResultString.s

*resultString = Libprocedure(myString_1, myString_2)
myResultString = *resultString\s
LibFreeString(*resultString)
Other options would include kandl's or passing a pointer to the Libprocedure() that points to a preallocated buffer or string. The buffer pointer would then be used to store the string data through poking the values into the buffer and reading them out when the procedure completes.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 8:21 pm
by Marlin
Demivec wrote:@Marlin: Thanks for your description and explanation. I haven't needed to write code for DLL's or anything similar yet so please excuse me if I misunderstand.

Would a solution like the following work:

Have the library procedure 'Libprocedure()', return a pointer to a string or string structure that has it's memory allocated by the Libprocedure(). After the calling code has copied the string data it then calls another library procedure 'LibFreeString()' that frees the memory that was previously allocated.
I believe such an approach could work, but as it uses a String structure to catch the results anyways,
it could just be rewritten to:

Code: Select all

ProcedureCDLL Libprocedure(inparameter1.s, inparameter2.s, *pOutString.String)

    ;sample string results
    *pOutString\s = inparameter1 + inparameter2
 
  ProcedureReturn
EndProcedure


;example calling code

Define resultString.String, myString_1.s, myString_2.s, myResultString.s
...

Libprocedure(myString_1, myString_2, @resultString)
myResultString = resultString\s
I would like to use something like this:

Code: Select all

; ProcedureCDLL as above

; exampe calling code

Define myString_1.s, myString_2.s, myResultString.s
...

Libprocedure(myString_1, myString_2, AddressOf(myResultString))

; myResultString now contains the output :-)
; = done.
I did create a feature request for this:

Code: Select all

AddressOf(StringVar.s)
here, as there seems to be no real alternative available for that yet.

Re: How to obtain the address of a string variable?

Posted: Wed Jan 19, 2011 8:58 pm
by Demivec
Marlin wrote:I believe such an approach could work, but as it uses a String structure to catch the results anyways,
it could just be rewritten to:

Code: Select all

ProcedureCDLL Libprocedure(inparameter1.s, inparameter2.s, *pOutString.String)

    ;sample string results
    *pOutString\s = inparameter1 + inparameter2
 
  ProcedureReturn
EndProcedure


;example calling code

Define resultString.String, myString_1.s, myString_2.s, myResultString.s
...

Libprocedure(myString_1, myString_2, @resultString)
myResultString = resultString\s
I would like to use something like this:

Code: Select all

; ProcedureCDLL as above

; exampe calling code

Define myString_1.s, myString_2.s, myResultString.s
...

Libprocedure(myString_1, myString_2, AddressOf(myResultString))

; myResultString now contains the output :-)
; = done.
A point of conflict involves strings within a DLL. Strings within a DLL have their memory allocated on their own memory heap, separate from the calling code. This is why you can't return a string unless it's in a global variable of the DLL. After a DLL returned a string you would have to have it freed by the DLL. This prevents you from adding to it because this might cause it to have interaction with the wrong memory heap (i.e. code freeing library heap or heap freeing code heap).

Another point of conflict involves the string structure. I agree that it might be easier to have some way of accessing the 'hidden' structure of a '.s' string variable. That reminds me, thanks for the example 'hack' that accesses the string on the parameter stack. :wink: It a nice work around that also deals with the problem of string literals that are passed as parameters.