PDA

View Full Version : Unsolved Accessing a method's Return Value



JeremyJ
08-31-2011, 06:15 PM
I'm trying to access the return value of one of my methods in another method, but I cannot figure out how to call the return value. Here's an example:

-------------------------------------------------------------------------------------------------

PUB throttlePosition(statePin) : dutyCycle | throttleInput

'Purpose of this code is to detect the throttle input on "statePin" and output dutyCycle value between 0-100

dira[statePin] :=0
throttleInput := ina[statePin]

IF throttleInput == 0

dutyCycle := 10
IF throttleInput == 1

dutyCycle := 90
PUB speedControl

'in this method I want to call the 'throttlePosition' method for a specific pin and access the return value, which in this case is 'dutyCycle'

-------------------------------------------------------------------------------------------

What syntax should I use to do this? I cannot find anything in the Propeller Manual v1.0 that discusses how RValues for a certain method are accessed....

Mike Green
08-31-2011, 06:40 PM
throttlePosition has a value (like all methods). That's the return value. You'd do
PUB speedControl | aVariable
aVariable := throttlePosition(positionPin) ' where positionPin is a variable either in speedControl or globally

JeremyJ
08-31-2011, 07:34 PM
Thank-you for clearing that up for me.

Beau Schwabe
08-31-2011, 11:23 PM
JeremyJ,

A derivative of what Mike showed you, if you need to access multiple variables within a PUB, change...



PUB throttlePosition(statePin) : dutyCycle | throttleInput


...so that it reads ...



PUB throttlePosition(statePin) | dutyCycle , throttleInput

return @statePin '<- Make sure this is the last line of your PUB


...Then in your PUB speedControl you can access statePin, dutyCycle and throttleInput like this...



PUB speedControl | VarAddress, temp

VarAddress := throttlePosition(positionPin) '- Calls the Throttle PUB ;
'- positionPin is passed as statePin into
' the throttlePosition PUB

'long[VarAddress][0] holds the value of statePin within the throttlePosition PUB
'long[VarAddress][1] holds the value of dutyCycle within the throttlePosition PUB
'long[VarAddress][2] holds the value of throttleInput within the throttlePosition PUB

' i.e.

temp := long[VarAddress][1] 'temp now contains the value of duty cycle

long[VarAddress][1] := 40 'you just changed the duty cycle value to 40

Heater.
09-01-2011, 12:59 AM
Beau,
How can that work reliably? Aren't a methods local variables on the stack? Technically they do not exist after the method has returned and they may be overwritten by other activity before you come to use them. Or am I missing a point here?

Beau Schwabe
09-01-2011, 01:16 AM
Heater.

you are correct ... "Aren't a methods local variables on the stack? Technically they do not exist after the method has returned and they may be overwritten by other activity before you come to use them." ... If you want to read the values from the PUB you need to read them when you return. They will stay intact and not be overwritten as long as you don't call another PUB.



PUB LEDs | VarAddress1, VarAddress2

VarAddress1 := LEDtest1(0) ' This is the PUB we want to read from

'VarAddress2 := LEDtest2(0) ' Well call this just to throw a kink in things
' unremark this line and you can see how the read
' value is effected.

dira[23..16]~~
outa[23..16] := long[VarAddress1][2]

repeat


PUB LEDtest1(InputVar1) | localPUBvar1 , localPUBvar2

InputVar1 := %11110000 'long[VarAddress1][0]

localPUBvar1 := %10101010 'long[VarAddress1][1]

localPUBvar2 := %11100110 'long[VarAddress1][2]

return @InputVar1

PUB LEDtest2(InputVar2) | localPUBvar3 , localPUBvar4

InputVar2 := %11111111

localPUBvar3 := %00110011

localPUBvar4 := %10011001

return @InputVar2

JeremyJ
09-01-2011, 01:30 AM
OK, this is interesting. When Mike replied to the original post I was going to ask about a method that returned multiple values, but I assumed you would just use an array like this (expanding on his example, assuming method throttlePosition(positionPin) returns multiple values):

PUB speedControl | aVariable, bVariable, cVariable

aVariable := throttlePosition(positionPin) ' where positionPin is a variable either in speedControl or globally
bVariable := aVariable[0]
cVariable := aVariable[1]

....use some method to access the individual values that were passed with the array.

Beau Schwabe
09-01-2011, 01:39 AM
JeremyJ,

That method won't work because aVariable is local to speedControl ... I say that, but there is calculated offset based on the size of PUB speedControl upon compile time.... a way around that it to find the starting address of the PUB you want to read from.... placing a return within the PUB and using the @ modifier to return the address essentially locates the offset I mentioned earlier.

Heater.
09-01-2011, 01:44 AM
Beau,
Perhaps it works but it relies on a bunch of undocumented features of the Spin compiler:
1. A methods parameter(s) are passed on the stack. Not in registers for example.
2. A methods parameter(s) and local variables are laid out contiguously on the stack.
3. The stack grows upwards.

As these assumptions are undocumented it cannot be assumed that they will always work. For example if the compiler were changed.

Further, in your example you show setting a methods local var after it has returned. To what purpose? that is just now a slot on unused stack that may well be overwritten by subsequent method cals or othe stack using activity.

It would be better to do this by having the calling function opass a pointer to some of it's local variables to the method such that they are at least sure to exist when the called method returns. But even that is making some assumptions about the compiler and stack lay out.

Beau Schwabe
09-01-2011, 02:04 AM
Heater,

I akin local variables within a PUB to a method very similar to how the res works in assembly language.

Duane Degn
09-01-2011, 02:56 AM
This is all very interesting, but wouldn't be easier to just use some global variables?

It's nice to have methods that are self-contained but when you start accessing these local variables from another method they are hardly self-contained anymore and I'd think using a global variable would make the program easier to read and maintain. I think using global variables would probably use less memory than using multiple local variables to store the same data.

Is there really a need for these tricks? (I do think they are cool tricks and I'm glad to learn them.)

Duane

Dave Hein
09-01-2011, 03:17 AM
I would suggest not using Beau's method unless you really understand how Spin uses the stack. It is much safer to pass a pointer to an array, and have the called method write the return values into the array. Beau's method will work as long as you don't overwrite the data on the stack. A statement like "y := a + 5*long[ptr][3*x]" would load five longs on the stack and overwrite the results left by the called method.

Heater.
09-01-2011, 07:43 AM
Regardless of the underlying mechanics of the thing having a caller access the callees local variables like that is just creating a great deal of confusion that may well surface an bite you later. It's just a poor programming practice to be avoided unless you have a compelling need to use it.
Firstly if the way the compiler works changes with some future version (the Prop II version for example) then your code will fail in hard to figure out ways. Parallax should not be constrained to ensuring all such hacks work for all compilers forever.
Secondly you may one day change the called function, say adding or removing local vars, and then spend a lot of time wondering why your program does not work. There is nothing in the function to remind you of the dependencies you have created.

One place where this sort of trick is used to good effect is in the way Lonesock passes data in and out of his float 32 object. There it can be justified as a means of compacting code and gaining speed in a commonly used library function that is unlikely yo change much and is carefully documented.