Page 1 of 2
If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 1:28 am
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
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 2:23 am
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.

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 2:54 am
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.
If this frankenstein works for you great, otherwise back to the lab...

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 3:19 am
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
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 4:04 am
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.

Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 4:30 am
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.
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)
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 5:30 am
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)
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 6:24 am
by skywalk
Hi xorc1zt,
How are you keeping track of your allocated memory?
And will it work if in a nested call?
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 7:00 am
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)
now i can pick the address of the pointer *pstr
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
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 7:29 am
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
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 7:30 am
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"
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 8:13 am
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.

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).
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 8:26 am
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)
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 8:35 am
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
Re: If eax = *p, then FreeMemory(*p) before ProcedureReturn?
Posted: Thu Dec 22, 2011 9:31 am
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)