If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Just starting out? Need help? Post your questions and find answers here.
User avatar
skywalk
Addict
Addict
Posts: 4219
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by skywalk »

Hi,
I am trying to return a structure from a Procedure and then use it in a nested equation.
I've done this in VB6 given Functions can return types or structures or arrays and the 'Exit Function' command does not change the function's return value.

Code: Select all

Public Type cri   ' Define complex rectangular(real/imag) or polar(mag/ang)
  r As Double     ' real or mag
  i As Double     ' imag or ang
End Type
Public Function cSum(x As cri, y As cri) As cri
  ' Returns complex sum
  cSum.r = x.r + y.r
  cSum.i = x.i + y.i
  Exit Function ' shown but not needed
End Function
So in PB46 I created 2 Procedures.
Unless someone can explain how to preserve the eax value after calling FreeMemory(*p) in cSum2(), I am forced to use the 3 parameter Procedure cSum3().
Essentially, I would prefer not to pass a pointer to the Result as a parameter.
And I also don't want to use a Global *p.cri, which works fine but is harder to manage.
Any ideas?

Code: Select all

Structure cri   ; Define complex rectangular(real/imag) or polar(mag/ang)
  r.d   ; real or mag
  i.d   ; imag or ang
EndStructure
Define.cri A,B,*C = AllocateMemory(SizeOf(cri))

Procedure.i cSum2(*x.cri, *y.cri)
  ; Return complex sum of 2 complex numbers
  ; Returns eax Structured Pointer Memory.
  Protected.cri *p = AllocateMemory(SizeOf(cri))
  *p\r = *x\r + *y\r
  *p\i = *x\i + *y\i
  Debug "*p in cSum2() = " + Str(*p)
  !MOV eax,[p.p_p]
  ;FreeMemory(*p)   ; eax is correct if uncommented, but creates memory leak!
  ProcedureReturn   ; eax preserved
EndProcedure

Procedure.i cSum3(*x.cri, *y.cri, *r.cri)
  ; Return complex sum of 2 complex numbers
  ; Returns Pointer to Structured memory.
  *r\r = *x\r + *y\r
  *r\i = *x\i + *y\i
  ProcedureReturn *r
EndProcedure

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
*C\r = 0: *C\i = 0

Debug "--- Using cSum2() ---"
Debug "*C = " + Str(*C)
*C = cSum2(A,B)
Debug "[1+1i] + [2+2i] = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"

Debug "--- Using cSum3() ---"
*C = cSum3(A,B,*C)
Debug "[1+1i] + [2+2i] = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
*C\r = 0: *C\i = 0
Debug "--- Using nested call of cSum2() ---"
*C = cSum2(A, cSum2(A,B))
Debug "[1+1i] + ([1+1i] + [2+2i]) = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"
Debug "*C = " + Str(*C)

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
*C\r = 0: *C\i = 0
Debug "--- Using nested call of cSum3() ---"
*C = cSum3(A, cSum3(A,B,*C),*C)
Debug "[1+1i] + ([1+1i] + [2+2i]) = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"
Debug "*C = " + Str(*C)
If *c
  FreeMemory(*C)
EndIf
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Demivec
Addict
Addict
Posts: 4270
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by Demivec »

cSum2() should be written this way:

Code: Select all

Procedure.i cSum2(*x.cri, *y.cri)
  ; Return complex sum of 2 complex numbers
  ; Returns pointer to Structured Memory.
  Protected.cri *p = AllocateMemory(SizeOf(cri))
  *p\r = *x\r + *y\r
  *p\i = *x\i + *y\i
  Debug "*p in cSum2() = " + Str(*p)
  ProcedureReturn *p
  ; structure memory will have to be freed after the procedure call to prevent a memory leak!
EndProcedure
This won't change or improve the way it functions but will do away with the unnecessary parts of the code. As before, it will return a pointer that needs to be freed outside the procedure call. The necessity of freeing the pointer makes using this method in nested procedure calls difficult to accomplish.

I have a crude idea to deal with this that I'm going to write a test implementation for. If I get it working I will post the frankenstein procedure that results. :mrgreen:
User avatar
Demivec
Addict
Addict
Posts: 4270
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by Demivec »

Here is the frankenstein procedure:

Code: Select all

Procedure.i cSum2(*x.cri, *y.cri)
  ;Return complex sum of 2 complex numbers
  Static Dim results.cri(20) ;set dim size to include maximum concurrent copies needed
  Static nextResult = 0 ;points to index of next value to return a pointer for
  
  Protected.cri *p = @results(nextResult)
  ;InitializeStructure(*p, cri) ;this isn't needed since all values will be replaced by new ones
  nextResult = (nextResult + 1) % 21 ;rotate to next index
  *p\r = *x\r + *y\r
  *p\i = *x\i + *y\i
  Debug "*p in cSum2() = " + Str(*p)
  ProcedureReturn *p
  ;The number of values that can be pointed to concurrently is the same as the size of results().
  ;It is still a good idea to copy it if it needs to persist.
EndProcedure
All it does is make the procedure return a pointer to a stack of static values that remain at the same memory locations for the duration of the program's run. Each time the procedure is called it points to the next value. The size of the array of results can be set depending on the complexity of the nested calls required. A value of 100 would only use 160 bytes or so and should handle some serious nesting. :D

If this frankenstein works for you great, otherwise back to the lab... :)
User avatar
skywalk
Addict
Addict
Posts: 4219
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by skywalk »

Haha Demivec, your Frankenstein procedure has bolts coming out of my head?

So, no way I can get Igor to preserve the eax value with some fancy ASM code?
It is blown away when I call FreeMemory(*p) :(
The 3 parameter cSum3() is looking like the best compromise for nested equations.

But wait a minute!
You got me thinking with static ;)

Code: Select all

moved to later post...
Last edited by skywalk on Thu Dec 22, 2011 4:31 am, edited 2 times in total.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Demivec
Addict
Addict
Posts: 4270
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by Demivec »

skywalk wrote:Haha Demivec, your Frankenstein procedure has bolts coming out of my head?

So, no way I can get Igor to preserve the eax value with some fancy ASM code?
It is blown away when I call FreeMemory(*p) :(
I may have missed something but it seemed all you were doing is returning the pointer to the structure memory. If you really wanted to do that with some fancy ASM code it would work but why go through the fuss of doing that instead of just jusing 'ProcedureReturn *p' ? As to the FreeMemory() call, you shouldn't expect to get good results when you try to access memory that you just freed.
skywalk wrote:Question?
With Static Dim results.cri(20), can this only be released on program exit?
Yes it will only be released on program exit, though you can cheat some by Dim'ing the array within the procedure to a new size.
skywalk wrote:The 3 parameter cSum3() is looking like the best compromise for nested equations.
Yes, but as you said it also is a little messy.


Wish Igor a Merry Christmas for me. :wink:
User avatar
skywalk
Addict
Addict
Posts: 4219
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by skywalk »

Hi Demivec,
Doh, I edited my previous message before I read your reply.

What confused me is a non static double array of 2 elements was intact after the procedure exits.
But not a structured variable of 2 consecutive doubles?
The freememory() command must do more than null pointers for structures?
So, now using your Static idea I can just use a double array of 2 elements and receive it with a complex pointer. It's getting simpler.
But I would love a structure return or array return from procedures. 8)

Code: Select all

Structure cri   ; Define complex rectangular(real/imag) or polar(mag/ang)
  r.d   ; real or mag
  i.d   ; imag or ang
EndStructure
Define.cri A,B,C,*C = AllocateMemory(SizeOf(cri))

Procedure.i cSum2A(*x.cri, *y.cri)
  ; Return complex sum of 2 complex numbers
  ; Returns Pointer to Double Array that should be freed on Return.
  Static Dim r.d(1)   ; Must use Static to allow nested calls to this procedure
  ;                     and not clear the r() array contents
  r(0) = *x\r + *y\r
  r(1) = *x\i + *y\i
  ProcedureReturn @r()
EndProcedure

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
*C\r = 0: *C\i = 0
Debug "--- Using cSum2A() ---"
*C = cSum2A(A,B)
Debug "[1+1i] + [2+2i] = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
*C\r = 0: *C\i = 0
Debug "--- Using nested call of cSum2A() ---"
*C = cSum2A(A, cSum2A(A,B))
Debug "[1+1i] + ([1+1i] + [2+2i]) = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"
Debug "*C = " + Str(*C)

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
C\r  = 3: C\i  = 3
*C\r = 0: *C\i = 0
Debug "--- Using 3 nested calls of cSum2A() ---"
*C = cSum2A(A, cSum2A(cSum2A(A,C),B))
Debug "[1+1i] + ([1+1i] + [3+3i] + [2+2i]) = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"
Debug "*C = " + Str(*C)
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
xorc1zt
Enthusiast
Enthusiast
Posts: 276
Joined: Sat Jul 09, 2011 7:57 am

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by xorc1zt »

just for fun i wrote a ugly hack which work on windows xp 32 bits.

Code: Select all

Structure TEST
  a.i
  b.i
  c.i
EndStructure

Procedure.i additionstr(*a.TEST,*b.TEST)
  Define *struct = PeekI(@*a-4)
  *struct = PeekI(*struct+11)
  Define *temp.TEST = PeekI(*struct)
  
  If Not *temp
    *temp = AllocateMemory(SizeOf(TEST))
  EndIf
  
  *temp\a = *a\a + *b\a
  *temp\b = *a\b + *b\b
  *temp\c = *a\c + *b\c
  
  ProcedureReturn *temp
EndProcedure


Define aaa.TEST
With aaa
  \a = 2
  \b = 4
  \c = 6
EndWith

Define bbb.TEST
With bbb
  \a = 4
  \b = 6
  \c = 2
EndWith

Define *pstr.TEST
*pstr = additionstr( aaa, bbb )

Debug *pstr\a ; 6   (2 + 4)
Debug *pstr\b ; 10  (4 + 6 )
Debug *pstr\c ; 8   (6+2)
User avatar
skywalk
Addict
Addict
Posts: 4219
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by skywalk »

Hi xorc1zt,
How are you keeping track of your allocated memory?
And will it work if in a nested call?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
xorc1zt
Enthusiast
Enthusiast
Posts: 276
Joined: Sat Jul 09, 2011 7:57 am

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by xorc1zt »

this is what happen when you do *pstr = additionstr( aaa, bbb )

Code: Select all

0040107F  |. 68 F0424000    push 004042F0h                           ;  BBB.TEST
00401084  |. 68 E4424000    push 004042E4h                           ;  AAA.TEST
00401089  |. E8 D6000000    call 00401164h                           ;  additionstr( )
0040108E  |. A3 E0424000    mov dword ptr [004042E0h], eax           ;  Move the return value to @*pstr

first, i peek a integer at the address of the first argument - 4 bytes to get the return address (0040108E)

Code: Select all

Define *struct = PeekI(@*a-4)
now i can pick the address of the pointer *pstr

Code: Select all

*struct = PeekI(*struct+11)
finally, i read the value of the pointer which is where the structure is located. if the pointer has no value i must allocate memory

Code: Select all

Define *temp.TEST = PeekI(*struct)

If Not *temp
    *temp = AllocateMemory(SizeOf(TEST))
EndIf
xorc1zt
Enthusiast
Enthusiast
Posts: 276
Joined: Sat Jul 09, 2011 7:57 am

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by xorc1zt »

for nested calls we must use the stack and not pointers but purebasic allow only pointers for passing a structure as a argument

Code: Select all

Procedure anythinghere( aaa.test ) ; aaa will produce a syntax erro, must be declared as a pointer 
EndProcedure
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by wilbert »

Why not simply accept that PureBasic can't return structures from a procedure at this time ?
The best and most stable way to do it is simply your cSum3 procedure.
There's no need to allocate any memory yourself or to nest things.
To me it was most logical to put the result always as the first parameter.

Code: Select all

Structure cri   ; Define complex rectangular(real/imag) or polar(mag/ang)
  r.d   ; real or mag
  i.d   ; imag or ang
EndStructure
Define.cri R, A, B

Procedure.i cSum3(*r.cri, *x.cri, *y.cri)
  ; Return complex sum of 2 complex numbers
  *r\r = *x\r + *y\r
  *r\i = *x\i + *y\i
EndProcedure

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2

Debug "--- Using cSum3() ---"
cSum3(R, A, B)
Debug "[1+1i] + [2+2i] = " + StrD(R\r,1) + " + " +StrD(R\i,1) + "i"

Debug "--- Using cSum3() ---"
cSum3(R, A, B)
cSum3(R, A, R)
Debug "[1+1i] + ([1+1i] + [2+2i]) = " + StrD(R\r,1) + " + " +StrD(R\i,1) + "i"
User avatar
skywalk
Addict
Addict
Posts: 4219
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by skywalk »

wilbert wrote:Why not simply accept that PureBasic can't return structures from a procedure at this time ?
The best and most stable way to do it is simply your cSum3 procedure.
There's no need to allocate any memory yourself or to nest things.
To me it was most logical to put the result always as the first parameter.
What I mean by 'nesting' is multiple calls to a procedure on 1 line, where the return value is mixed in. It is way more readable and elegant than breaking up all my old code into simple procedure calls. And I don't want to have the Result values in the parameter lists when it is not in my code written soooo long ago in VB6. :(

Code: Select all

*C = cSum2A(A, cSum2A(cSum2A(A,C),B))
Often people malign VB6 for lots of different reasons, but in cases like this, a feature that was available to me in 1997 shocks me as odd when unavailable in an advanced compiler like PB46, 15 years later. :shock:
As for the order of parameters, it is only personal preference. I chose to follow the algebraic order: A + B = C --> (A,B,C)
Unless there is some flaw, I prefer the approach of the Static Double array in cSum2A(A,B).
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by wilbert »

skywalk wrote:Unless there is some flaw, I prefer the approach of the Static Double array in cSum2A(A,B).
Sure there's a problem.
See for yourself

Code: Select all

Structure cri   ; Define complex rectangular(real/imag) or polar(mag/ang)
  r.d   ; real or mag
  i.d   ; imag or ang
EndStructure
Define.cri A,B,C,*C = AllocateMemory(SizeOf(cri))

Procedure.i cSum2A(*x.cri, *y.cri)
  ; Return complex sum of 2 complex numbers
  ; Returns Pointer to Double Array that should be freed on Return.
  Static Dim r.d(1)   ; Must use Static to allow nested calls to this procedure
  ;                     and not clear the r() array contents
  r(0) = *x\r + *y\r
  r(1) = *x\i + *y\i
  ProcedureReturn @r()
EndProcedure

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
C\r  = 3: C\i  = 3
*C\r = 0: *C\i = 0
Debug "--- Using 3 nested calls of cSum2A() ---"
*C = cSum2A(cSum2A(A,B), cSum2A(A,C))
Debug "([1+1i] + [2+2i]) + ([1+1i] + [3+3i]) = " + StrD(*C\r,1) + " + " +StrD(*C\i,1) + "i"
Debug "*C = " + Str(*C)
xorc1zt
Enthusiast
Enthusiast
Posts: 276
Joined: Sat Jul 09, 2011 7:57 am

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by xorc1zt »

about the static array

Code: Select all

Structure cri   ; Define complex rectangular(real/imag) or polar(mag/ang)
  r.d   ; real or mag
  i.d   ; imag or ang
EndStructure
Define.cri A,B,C,*C = AllocateMemory(SizeOf(cri))

Procedure.i cSum2A(*x.cri, *y.cri)
  ; Return complex sum of 2 complex numbers
  ; Returns Pointer to Double Array that should be freed on Return.
  Static Dim r.d(1)   ; Must use Static to allow nested calls to this procedure
  ;                     and not clear the r() array contents
  r(0) = *x\r + *y\r
  r(1) = *x\i + *y\i
  ProcedureReturn @r()
EndProcedure

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
C\r  = 3: C\i  = 3
*C\r = 0: *C\i = 0


Define *D.cri = AllocateMemory(SizeOf(cri))

*C = cSum2A(A,B)

Debug " "
Debug "C:"
Debug *C\i
Debug *C\r

*D = cSum2A(A,C)

Debug " "
Debug "D:"
Debug *D\i
Debug *D\r
Debug " "
Debug "C:"
Debug *C\i
Debug *C\r
Debug " "
Debug "*C = 0x" + Hex(*C)
Debug "*D = 0x" + Hex(*D)
*D = cSum2A(A,C) overwrite *C because they share the same array
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?

Post by wilbert »

If you insist on 2 arguments instead of 3 it makes sense to add a flag to retain a value

Code: Select all

Structure cri   ; Define complex rectangular(real/imag) or polar(mag/ang)
  r.d   ; real or mag
  i.d   ; imag or ang
  retain.l
EndStructure

Global Dim TempCriArr.cri(1023)
Global TempCriIdx = 0

Define.cri A, B, C, *R

; retain value
Procedure retain(*p.cri)
  *p\retain = #True
EndProcedure

; release value
Procedure release(*p.cri)
  *p\retain = #False
EndProcedure

; find next free cri pointer
Procedure freeCriPtr()
  Protected *p.cri
  Repeat
    TempCriIdx = (TempCriIdx + 1) % 1024
    *p = @TempCriArr(TempCriIdx)
  Until *p\retain = #False  
  ProcedureReturn *p
EndProcedure

Procedure.i cSum2(*x.cri, *y.cri)
  
  Protected *p.cri = freeCriPtr()
  
  *p\r = *x\r + *y\r
  *p\i = *x\i + *y\i
  ProcedureReturn *p
  
EndProcedure

A\r  = 1: A\i  = 1
B\r  = 2: B\i  = 2
C\r  = 3: C\i  = 3

Debug "--- Using nested calls of cSum2() ---"
*R = cSum2(cSum2(A, B), cSum2(A, C))
retain(*R)

Debug "([1+1i] + [2+2i]) + ([1+1i] + [3+3i]) = " + StrD(*R\r,1) + " + " +StrD(*R\i,1) + "i"

release(*R)
Post Reply