Announcement

Collapse

New Sub-Forum

In an effort to help make sure there are appropriate categories for topics of discussion that are happening, there is now a sub-forum for databases and database programming under Special Interest groups. Please direct questions, etc., about this topic to that sub-forum moving forward. Thank you.
See more
See less

Parameter as Reply

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Parameter as Reply

    Often I see API functions that one or more of the parameters passed into the function, is a reply that you need back (besides the actual reply) but I am unsure how I would do the same in PB (if its even possible).

    Since this statement sounds counter-intuitive, below is a sample of what I am trying to figure out.
    Code:
    #COMPILE EXE
    #DIM ALL
    DECLARE FUNCTION TestReplies(MyStringIn AS STRING, MyStringRemain AS STRING)AS STRING
    FUNCTION PBMAIN () AS LONG
       LOCAL MyReply AS STRING
       LOCAL MyRemain AS STRING
       
       MyReply = TestReplies("HELLO", "")
       MyRemain = MyStringRemain
       MSGBOX MyReply
       MSGBOX MyRemain
    END FUNCTION
    
    FUNCTION TestReplies(MyStringIn AS STRING, MyStringRemain AS STRING)AS STRING
         MyStringRemain = MID$(MyStringIn, 3, 2)
         FUNCTION = MID$(MyStringIn, 4, 1)
    END FUNCTION
    In this example what I need is
    1. The Reply from the function (obvious answer of course)
    2. What is left of the string in "MyStringRemain" (Could be the length of whats left, or something else, but for simplicity, I just wanted whats left etc..)

    I know its all pseudo-code, since the idea I had does not work (obviously). So I wondered how would I make it work?
    Engineer's Motto: If it aint broke take it apart and fix it

    "If at 1st you don't succeed... call it version 1.0"

    "Half of Programming is coding"....."The other 90% is DEBUGGING"

    "Document my code????" .... "WHYYY??? do you think they call it CODE? "

  • #2
    Getting the results of more than one argument in a function is just like a sub, unless, of course, the argument is passed BYVAL.

    For example:

    Function A( B, C, D) as long

    B = B + 1
    C = C + 2
    D = D + 3

    function = D
    End Function

    The results for B and C are contained in the function argument.
    Walt Decker

    Comment


    • #3
      Either I misunderstand or you do, so using your example I modified to
      Code:
      #COMPILE EXE
      #DIM ALL
      
      DECLARE FUNCTION A(B AS LONG, C AS LONG, D AS LONG) AS LONG
      '*** Tried this just in case it worked for some reason
      'global B AS LONG, C AS LONG, D AS LONG
      
      FUNCTION PBMAIN () AS LONG
        DIM MyTest AS LONG
        MyTest = A(1, 2, 3)               '<--- Pass Parameters and expect they change on return (obviously wrong thought)
        MSGBOX STR$(MyTest)               '<--- Valid in this function because it is the reply from Function A
      '  MSGBOX STR$(B)                   '<--- InValid in this function (as a reply to the parameter I passed into it)
      '  MSGBOX STR$(C)                   '<--- InValid in this function (as a reply to the parameter I passed into it)
      '  MSGBOX STR$(D)                   '<--- InValid in this function (as a reply to the parameter I passed into it)
        
          
      
      END FUNCTION
      FUNCTION A( B AS LONG, C AS LONG, D AS LONG) AS LONG
      
      B = B + 1                           '<--- Valid in this function because it is a parameter passed
      C = C + 2                           '<--- Valid in this function because it is a parameter passed
      D = D + 3                           '<--- Valid in this function because it is a parameter passed
      
      FUNCTION = D                        '<--- Valid in this function because it is passed back to the function that called it
      END FUNCTION
      As you uncomment lines you will see it will not compile, Much less even work.
      However if it were an API call that allowed it, I could find the value of C as Well as D if those were the answers I was after.

      Sure I could go about things differently and check the value separately, but was curious how an API call I could check directly
      (best guess is something to do with pointers)
      Engineer's Motto: If it aint broke take it apart and fix it

      "If at 1st you don't succeed... call it version 1.0"

      "Half of Programming is coding"....."The other 90% is DEBUGGING"

      "Document my code????" .... "WHYYY??? do you think they call it CODE? "

      Comment


      • #4
        Code:
        #COMPILE EXE
        #DIM ALL
        
        DECLARE FUNCTION A(B AS LONG, C AS LONG, D AS LONG) AS LONG
        
        FUNCTION PBMAIN () AS LONG
          DIM MyTest AS LONG
          DIM B AS LONG
          DIM C AS LONG
          DIM D AS LONG
          B = 1
          C = 2
          D = 3
          MyTest = A(B, C, D)               '<--- Pass Parameters and expect they change on return (obviously wrong thought)
          MSGBOX STR$(MyTest)               '<--- Valid in this function because it is the reply from Function A
          MSGBOX STR$(B)                   '<--- InValid in this function (as a reply to the parameter I passed into it)
          MSGBOX STR$(C)                   '<--- InValid in this function (as a reply to the parameter I passed into it)
          MSGBOX STR$(D)                   '<--- InValid in this function (as a reply to the parameter I passed into it)
          
        END FUNCTION
        FUNCTION A( B AS LONG, C AS LONG, D AS LONG) AS LONG
        
        B = B + 1                           '<--- Valid in this function because it is a parameter passed
        C = C + 2                           '<--- Valid in this function because it is a parameter passed
        D = D + 3                           '<--- Valid in this function because it is a parameter passed
        
        FUNCTION = D                        '<--- Valid in this function because it is passed back to the function that called it
        END FUNCTION
        Forum: http://www.jose.it-berater.org/smfforum/index.php

        Comment


        • #5
          Of course it doesn't work, Cliff. In the first place you're passing literals instead of variables. You can't change the value of a literal in a call argument.

          In the second place, you have #DIM ALL on and you haven't declared all your variables. Jose' pointed that out with his example.

          Personally, if I wanted more than one result from a routine I'd use a SUB. But there are a number of different ways to get multiple results from a routine: structures and pointers come to mind. You can even use a pointer to a structure of pointers or pointers to pointers to pointers.
          Walt Decker

          Comment


          • #6
            Code:
             
            #COMPILE EXE
            #DIM ALL
            DECLARE FUNCTION TestReplies(MyStringIn AS STRING, MyStringRemain AS STRING)AS STRING
            FUNCTION PBMAIN () AS LONG
               LOCAL MyReply AS STRING
               LOCAL MyRemain AS STRING
               MyReply = TestReplies("HELLO", MyRemain)
            '   MyRemain = MyStringRemain
               MSGBOX MyReply
               MSGBOX MyRemain
            END FUNCTION
            FUNCTION TestReplies(MyStringIn AS STRING, MyStringRemain AS STRING)AS STRING
                 MyStringRemain = MID$(MyStringIn, 3, 2)
                 FUNCTION = MID$(MyStringIn, 4, 1)
            END FUNCTION
            Regards,
            Bob

            Comment


            • #7
              Win APIs are often constructed the way that the return value of a function indicates failure or success of the function's operation, whereas one or more of the parameters receive the result(s) of the function's work.

              Comment


              • #8
                Thanx guys

                I modified the 2 examples and over-commented them in case someone else out there is wondering the same thing.

                Longs
                Code:
                #COMPILE EXE
                #DIM ALL
                #INCLUDE "Win32Api.inc"
                
                DECLARE FUNCTION A(B AS LONG, C AS LONG, D AS LONG) AS LONG
                
                FUNCTION PBMAIN () AS LONG
                     LOCAL MyTest AS LONG          'Declare variable
                     LOCAL B AS LONG               'Declare variable
                     LOCAL C AS LONG               'Declare variable
                     LOCAL D AS LONG               'Declare variable
                     B = 1                         'Fill Variable with a value
                     C = 2                         'Fill Variable with a value
                     D = 3                         'Fill Variable with a value
                '     MyTest = A(1, 2, 3)           '<--- Values can not be passed as literals if wanting variable values on return
                     MyTest = A(B, C, D)           '<--- Pass Parameters (on return variables have a new value)
                     MSGBOX FUNCNAME$ + $CR _
                               + "Function Passed = " + STR$(MyTest) + $CR _
                               + "B was 1 now it is " + STR$(B) + $CR _
                               + "C was 2 now it is " + STR$(C) + $CR _
                               + "D was 3 now it is " + STR$(D)
                END FUNCTION
                
                FUNCTION A( B AS LONG, C AS LONG, D AS LONG) AS LONG
                     ON ERROR GOTO ErrHandler
                     B = B + 1                     'Change variable value
                     C = C + 2                     'Change variable value
                     D = D + 3                     'Change variable value
                     FUNCTION = %True              'Return %True if function passed
                     EXIT FUNCTION                 'Exit function so correct value returned
                ErrHandler:                        'If error then jump to here
                     FUNCTION = %False             'If error then return %False to indicate the function failed
                END FUNCTION
                Strings
                Code:
                #COMPILE EXE
                #DIM ALL
                #INCLUDE "Win32Api.inc"
                
                DECLARE FUNCTION TestReplies(MyStringIn AS STRING, MyStringRemain AS STRING)AS LONG
                
                FUNCTION PBMAIN () AS LONG
                     LOCAL MyReply AS LONG
                     LOCAL MyStringIn AS STRING
                     LOCAL MyStringRemain AS STRING
                     MyStringIn = "HELLO"
                '     MyReply = TestReplies("HELLO", MyStringRemain)         'Passing literal then only MyStringRemain has a value on return
                     MyReply = TestReplies(MyStringIn, MyStringRemain)      'Passing variables gets correct reply
                     MSGBOX FUNCNAME$ + $CR _
                               + "Function Passed = " + STR$(MyReply) + $CR _
                               + "MyStringIn was HELLO now it is " + MyStringIn + $CR _
                               + "MyStringRemain was empty string now it is " + MyStringRemain
                END FUNCTION
                
                FUNCTION TestReplies(MyStringIn AS STRING, MyStringRemain AS STRING)AS LONG
                     ON ERROR GOTO ErrHandler
                     MyStringRemain = MID$(MyStringIn, 3, 2)                'Change variable value
                     MyStringIn = MID$(MyStringIn, 4, 1)                    'Change variable value
                     FUNCTION = %True                                       'Return %True if function passed
                     EXIT FUNCTION                                          'Exit function so correct value returned
                ErrHandler:                                                 'If error then jump to here
                     FUNCTION = %False                                      'If error then return %False to indicate the function failed
                END FUNCTION
                Understanding these principles will go a long way towards my way of thinking when I code, and eliminating the use of GLOBALS
                (Yes I know I am just inviting a comment about Globals vs Locals with that one, but in this case its worth it )

                Thanks again guys
                Engineer's Motto: If it aint broke take it apart and fix it

                "If at 1st you don't succeed... call it version 1.0"

                "Half of Programming is coding"....."The other 90% is DEBUGGING"

                "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                Comment


                • #9
                  Just a minor point, Cliff, successful API routines normally return zero to indicate no errors.

                  For consistencies sake your TestReplies routine, if you like, will be

                  Code:
                  FUNCTION TestReplies(MyStringIn AS STRING, MyStringRemain AS STRING)AS LONG
                    ON ERROR GOTO ErrHandler
                    MyStringRemain = MID$(MyStringIn, 3, 2) 'Change variable value
                    MyStringIn = MID$(MyStringIn, 4, 1)     'Change variable value
                    EXIT FUNCTION                           'Exit function so correct value returned
                  ErrHandler:                               'If error then jump to here
                    FUNCTION = %True                        'If error then return %True to indicate the function failed
                  END FUNCTION
                  A FUNCTION is a variable and if undefined defaults to zero, in this case, so there's no need for %False.

                  Comment


                  • #10
                    You're welcome, Cliff.

                    You should also know the difference between BYVAL (by value) and BYREF (by reference). The following are examples of both:

                    SUB A(BYVAL B AS LONG, BYREF C AS INTEGER, D AS SINGLE)

                    Parameter "B" is passed like a literal, it can be changed in the procedure, but it is not changed on return from the procedure. However, if it is the "address" of a variable that variable can be changed in the procedure.

                    Parameter "C" is actually a "pointer" to its address and on return is changed.

                    Parameter "D" is an implyed BYREF pass and the same as "C".

                    Another thing you might want to investigate is the DEF statement. Personally I normally use that to define integer class variables and hand define all other type variables.
                    Walt Decker

                    Comment


                    • #11
                      I was working on things a step farther, to see what it would take for arrays being passed.

                      @Walt Decker
                      You should also know the difference between BYVAL (by value) and BYREF (by reference). The following are examples of both:

                      SUB A(BYVAL B AS LONG, BYREF C AS INTEGER, D AS SINGLE)

                      Parameter "B" is passed like a literal, it can be changed in the procedure, but it is not changed on return from the procedure. However, if it is the "address" of a variable that variable can be changed in the procedure.

                      Parameter "C" is actually a "pointer" to its address and on return is changed.

                      Parameter "D" is an implyed BYREF pass and the same as "C".
                      Programming 101...How in the world could I have not thought of that? :doh:
                      but then again I am what MCM would call a VB Refugee, where they don't TEACH why ByVal or ByRef, but rather just tell you to use one or the other. But still I should have thought of that.

                      It would also explain why in the back of my head I thought I would have to pass pointers to arrays as parameters to work, but my sample below somehow worked. (Because in reality I was passing BYREF and not thinking about it)

                      @David Roberts
                      successful API routines normally return zero to indicate no errors.
                      Good point. Maybe not all API routines do, but on that thought it gave me an idea to "Beef Up" my functions to return the error code, and not just a "Ooops, some error occurred, but not saying why" type of return.
                      So thank you for pointing that out.

                      @Walt Decker
                      SUB A(BYVAL B AS LONG, BYREF C AS INTEGER, D AS SINGLE)

                      Parameter "B" is passed like a literal, it can be changed in the procedure, but it is not changed on return from the procedure.
                      Just for clarification I included a lil test in Sample1 below to test that.

                      Sample1 - Longs
                      Code:
                      #COMPILE EXE
                      #DIM ALL
                      #INCLUDE "Win32Api.inc"
                      
                      DECLARE FUNCTION A(BYVAL B AS LONG, C AS LONG, D AS LONG) AS LONG
                      
                      FUNCTION PBMAIN () AS LONG
                           LOCAL MyTest AS LONG          'Declare variable
                           LOCAL B AS LONG               'Declare variable
                           LOCAL C AS LONG               'Declare variable
                           LOCAL D AS LONG               'Declare variable
                           LOCAL TempB AS LONG
                           B = 1                         'Fill Variable with a value
                           TempB = B
                           C = 2                         'Fill Variable with a value
                           D = 3                         'Fill Variable with a value
                      '     MyTest = A(1, 2, 3)           '<--- Values can not be passed as literals if wanting variable values on return
                           MyTest = A(B, C, D)           '<--- Pass Parameters (on return variables have a new value)
                           MSGBOX "Function = " + FUNCNAME$ + $CR _
                                     + "Function Passed = " + STR$(MyTest) + $CR _
                                     + "B was " + STR$(TempB) + " now it is " + STR$(B) + $CR _
                                     + "C was 2 now it is " + STR$(C) + $CR _
                                     + "D was 3 now it is " + STR$(D)
                      END FUNCTION
                      
                      FUNCTION A( BYVAL B AS LONG, C AS LONG, D AS LONG) AS LONG
                           ON ERROR GOTO ErrHandler
                           LOCAL TempB AS LONG
                           TempB = B
                           B = B + 1                     'Change variable value        '<--- If Byval then B only changes in this function, not on return
                      MSGBOX "Function = " + FUNCNAME$ + $CR + "B was " + STR$(TempB) + $CR + "B is now " + STR$(B)
                           C = C + 2                     'Change variable value
                           D = D + 3                     'Change variable value
                           FUNCTION = %False             'Return %False if no errors   '<--- Not really needed but kept for readability
                           EXIT FUNCTION                 'Exit function so correct value returned
                      ErrHandler:                        'If error then jump to here
                           FUNCTION = ERR                'If error then return ErrorNumber
                      END FUNCTION
                      Now in that sample, if you take out the BYVAL then you see B is 2 on return, but with the BYVAL that B is only 2 while in FunctionA


                      Sample 2 started out as just simple arrays, but I had quickly jumped to including User Defined Types
                      Sample2 - Arrays with UDT's
                      Code:
                      #COMPILE EXE
                      #DIM ALL
                      #INCLUDE "Win32Api.inc"
                      
                      TYPE Apples
                           AppleSize AS LONG
                           AppleName AS ASCIIZ * %MAX_PATH
                      END TYPE
                      
                      DECLARE FUNCTION TestReplies(MyStringIn() AS Apples, MyStringRemain() AS Apples)AS LONG
                      
                      
                      FUNCTION PBMAIN () AS LONG
                           LOCAL MyReply AS LONG
                           DIM MyStringIn() AS Apples
                           DIM MyStringRemain() AS Apples
                           LOCAL i AS LONG
                           
                           REDIM MyStringIn(0 TO 1)
                           MyStringIn(0).AppleSize = 1
                           MyStringIn(0).AppleName = "MacIntosh"
                           MyStringIn(1).AppleSize = 3
                           MyStringIn(1).AppleName = "Summer"
                           MyReply = TestReplies(MyStringIn(), MyStringRemain())      'Passing variables gets correct reply
                           REDIM PRESERVE MyStringRemain(UBOUND(MyStringRemain))
                           FOR i = LBOUND(MyStringIn) TO UBOUND(MyStringIn)
                                MSGBOX FUNCNAME$ + $CR _
                                          + "Function Passed = " + STR$(MyReply) + $CR _
                                          + "MyStringIn" + STR$(i) + ".AppleSize = " + STR$(MyStringIn(i).AppleSize) + $CR _
                                          + "MyStringIn" + STR$(i) + ".AppleName = " + MyStringIn(i).AppleName + $CR _
                                          + "MyStringRemain" + STR$(i) + ".AppleSize = " + STR$(MyStringRemain(i).AppleSize) + $CR _
                                          + "MyStringRemain" + STR$(i) + ".AppleName = " + MyStringRemain(i).AppleName
                           NEXT i
                      END FUNCTION
                      
                      FUNCTION TestReplies(MyStringIn() AS Apples, MyStringRemain() AS Apples)AS LONG
                           ON ERROR GOTO ErrHandler
                           LOCAL i AS LONG
                           FOR i = LBOUND(MyStringIn) TO UBOUND(MyStringIn)
                                REDIM PRESERVE MyStringRemain(i)
                                MyStringIn(i).AppleSize = MyStringIn(i).AppleSize + 1               'Change variable value
                                MyStringIn(i).AppleName = STRREVERSE$(MyStringIn(i).AppleName)                'Change variable value
                                MyStringRemain(i).AppleSize = MyStringIn(i).AppleSize + 5               'Change variable value
                                MyStringRemain(i).AppleName = STRREVERSE$(MyStringIn(i).AppleName)                'Change variable value
                           NEXT i
                           FUNCTION = %False             'Return %False if no errors   '<--- Not really needed but kept for readability
                           EXIT FUNCTION                 'Exit function so correct value returned
                      ErrHandler:                        'If error then jump to here
                           FUNCTION = ERR                'If error then return ErrorNumber
                      END FUNCTION
                      Now in that sample, if you even TRY to pass an array of UDT's BYVAL the compiler will not let you. Which makes sense because I know I read in the manual that you have to pass pointers to the UDT's and not the UDT itself (For lack of a better word)


                      Thanx again guys, Simple clarifications on stuff I read lonnnnng ago and did not think about when trying to cement an idea in my head.

                      Anyways, I am sure if there is a VB Refugee reading all this, your help and comments will go a LONNNNNnnnnng way for them to easily understand, as well as reminding me of concepts that I was not thinking of (or did not fully understand when I read it in the manual)

                      also Walt your statement about checking out the %DEF statement just gave me another idea how to simplify my code that in the past I had multiple INC files, and Multiple "Header" files (*.h files that held all my declares so if 1 INC file relied on a Variable declared in another INC file, I could move the variable to a header file, and just load all my declares before loading my INC files)

                      Now I can almost get rid of all Globals and interdependencies between my INC files.

                      Thanx guys, you ROCK!!!!
                      Engineer's Motto: If it aint broke take it apart and fix it

                      "If at 1st you don't succeed... call it version 1.0"

                      "Half of Programming is coding"....."The other 90% is DEBUGGING"

                      "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                      Comment


                      • #12
                        Walt your statement about checking out the %DEF statement just gave me another idea how to simplify my code
                        It isn't %DEF, it's DEF[variable type]. For example:

                        DEFLNG A - Z automatically produces long integers for all variable names beginning with "A" to "Z".

                        DEFBYTE A, C, D automatically produces byte variables beginning with "A", "C", and "D".

                        Don't get confused with passing UDTs BYVAL vs. BYREF. Remember, you can pass an element of a UDT BYVAL and BYREF just as you can pass an element of a normal array by the same methods. Also, you cannot pass an entire "normal" array BYVAL. And, if you think about it, a UDT is just an array in which the various fields are specially defined.

                        Don't get to enthusiastic about eliminating globals. You'll find that in many instances globals are necessary, especially in callback functions since your code should not/cannot invoke a callback function directly.

                        Also, judicious use of globals can speed up applications because every time you invoke a procedure with an argument list the values or pointers must be pushed onto the stack before the procedure executes and must be poped off the stack when the procedure completes. These extra steps take extra time. That's one reason game programmers 1) use globals and 2) use as few procedures as possible (i.e. the "unroll" code).
                        Last edited by Walt Decker; 13 Dec 2007, 02:43 PM.
                        Walt Decker

                        Comment


                        • #13
                          Don't get to enthusiastic about eliminating globals. You'll find that in many instances globals are necessary, especially in callback functions since your code should not/cannot invoke a callback function directly.
                          Well maybe not TOTALLY eliminate globals, but minimize the number of globals I am currently using.
                          In my larger apps, from time to time I get odd results, or the compiler saying a duplicate Variable or Equates exists, and does not tell me where I may have declared it the second place (could be in a includes file I wrote years ago, and just now happened to use the same Variable name not realizing it), so I have to have windows do a search in every one of my files for that term and then go through each file till I find it.
                          (1 point against globals)

                          (1 point for globals) -
                          judicious use of globals can speed up applications because every time you invoke a procedure with an argument list the values or pointers must be pushed onto the stack before the procedure executes and must be poped off the stack when the procedure completes.
                          it all boils down to "What is best?" and the answer is "It depends"
                          or more to the point the answer is "What is the point of diminishing returns?"

                          Anyways thanx for all the input I am currently working on a block of code I wrote a while back and used many globals (and confusing to follow now that I have not touched it in months) just to see if I can cut down from 7 files, to 3 files (if my guess is right), and see if I have any speed decrease I can see, or if this way may be faster and easier to follow when done?
                          Engineer's Motto: If it aint broke take it apart and fix it

                          "If at 1st you don't succeed... call it version 1.0"

                          "Half of Programming is coding"....."The other 90% is DEBUGGING"

                          "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                          Comment


                          • #14
                            Just a minor point, Cliff, successful API routines normally return zero to indicate no errors.
                            I asked a while ago what is the convention for Function return values and as I recall the consensus was that -ve = error, 0=Not an error, +ve=Success.

                            I have used this convention for a while now and with very few exceptions it is compatile with Win API returns etc.

                            Comment


                            • #15
                              Mike,
                              what is ve? (I assume you meant ve = value)
                              although most the time I agree with you, if negative an error, if 0 no error, if positive then success, but to the strictest sense of True vs False (or success vs error), Windows describes it as True = Non-zero, and False as Zero

                              Although I am hard pressed to think of a good example that "True" had a negative number at the moment (***NOTE*** I said "Hard Pressed" not that I have not seen it before)

                              But I do know of Compilers that True = -1 and False = 0 so if preparing for another language (or even using a dll compiled in another language) True could be anything non-positive
                              Engineer's Motto: If it aint broke take it apart and fix it

                              "If at 1st you don't succeed... call it version 1.0"

                              "Half of Programming is coding"....."The other 90% is DEBUGGING"

                              "Document my code????" .... "WHYYY??? do you think they call it CODE? "

                              Comment

                              Working...
                              X