Shop OBEX P1 Docs P2 Docs Learn Events
Accessing a method's Return Value — Parallax Forums

Accessing a method's Return Value

JeremyJJeremyJ Posts: 30
edited 2011-08-31 23:43 in General Discussion
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....

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2011-08-31 10:40
    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
    
  • JeremyJJeremyJ Posts: 30
    edited 2011-08-31 11:34
    Thank-you for clearing that up for me.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2011-08-31 15:23
    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.Heater. Posts: 21,230
    edited 2011-08-31 16:59
    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 SchwabeBeau Schwabe Posts: 6,568
    edited 2011-08-31 17:16
    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
    
  • JeremyJJeremyJ Posts: 30
    edited 2011-08-31 17:30
    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 SchwabeBeau Schwabe Posts: 6,568
    edited 2011-08-31 17:39
    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.Heater. Posts: 21,230
    edited 2011-08-31 17:44
    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 SchwabeBeau Schwabe Posts: 6,568
    edited 2011-08-31 18:04
    Heater,

    I akin local variables within a PUB to a method very similar to how the res works in assembly language.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2011-08-31 18:56
    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 HeinDave Hein Posts: 6,347
    edited 2011-08-31 19:17
    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.Heater. Posts: 21,230
    edited 2011-08-31 23:43
    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.
Sign In or Register to comment.