Home AMX User Forum AMX General Discussion

Passing string by ref in Function

How do I write a function to pass string by ref in it ...

I string putting parameter variable as char, it giving an error, char[] also giving an error and string as variable type also giving an error.

Can someone advise me?

Thanks in advance ...

Comments

  • ericmedleyericmedley Posts: 4,177
    please post your current code and we'll see if we can help.
  • jweatherjweather Posts: 320
    DEFINE_FUNCTION foobar(CHAR param[])
    

    The type is CHAR, the brackets go after the parameter name. All parameters are passed by reference.
  • Thanks

    Thanks for your response ... This is working fine ...
    jweather wrote: »
    DEFINE_FUNCTION foobar(CHAR param[])
    

    The type is CHAR, the brackets go after the parameter name. All parameters are passed by reference.
  • jweather wrote: »
    DEFINE_FUNCTION foobar(CHAR param[])
    

    The type is CHAR, the brackets go after the parameter name. All parameters are passed by reference.
    Are you sure about that? I'm pretty certain that Netlinx passes method parameters as values, not references.

    Consider the following:
    define_function integer cubeit(integer x) {
      return x*x*x;
    }
    define_event
    button_event[10101:1:0,1] {
      push: {
    	stack_var integer x;
    	stack_var integer y;
    	
    	x = 3;
    	
    	y = cubeit(x);
    	// If the parameter was sent by reference,
    	// then both x and y would now carry a value
    	// of nine. But if the function carried the
    	// parameter by value, then x would be
    	// unaffected and therefore only y would
    	// equal nine.
      }
    }
    
    Am I wrong in my thinking?
  • DHawthorneDHawthorne Posts: 4,584
    It definitely passes by reference. I can't begin to tell you how many times that bit me in the behind when learning NetLinx after coming from C++. In your example, you are passing a stack variable, so once it goes out of scope, it must be creating a copy.
  • jweatherjweather Posts: 320
    Are you sure about that? I'm pretty certain that Netlinx passes method parameters as values, not references.

    Reasonably sure. From the help file for "Parameters subroutines":
    Parameters may be passed to any NetLinx function or subroutine. Calling parameters are simply variables or constants that originate from the caller and are received by the function or subroutine being invoked.

    The NetLinx compiler passes all variables by reference. This means that the variable the subroutine operates on is the same variable the caller passed. Any change made to a variable passed as a calling parameter updates the value of the variable from the perspective of the caller. You can take advantage of this pass by reference feature to return an updated value through a calling parameter rather than as the return value.

    Constants, on the other hand, are passed by value. When this happens, a copy of the parameter is delivered to the subroutine. Any change made to the variable representing the constant is lost once the function or subroutine finishes.

    Your cubeit example never modifies its parameter (x), so it won't test this fact. I tend to avoid modifying parameters since it makes it hard to see what's happening. The only reason I would use it is for a function that needs to return multiple values, in which case the modified parameters would be designated as "out" parameters. Otherwise the function can just return the single value that it produces.
    DEFINE_FUNCTION squareAndCube(INTEGER x, INTEGER outSquare, INTEGER outCube) {
      outSquare = x*x
      outCube = x*x*x
    }
    
    [...]
    INTEGER returnSquare, returnCube
    
    squareAndCube(42, returnSquare, returnCube)
    
  • a_riot42a_riot42 Posts: 1,624
    jweather wrote: »
    DEFINE_FUNCTION squareAndCube(INTEGER x, INTEGER outSquare, INTEGER outCube) {
      outSquare = x*x
      outCube = x*x*x
    }
    
    [...]
    INTEGER returnSquare, returnCube
    
    squareAndCube(42, returnSquare, returnCube)
    

    Please don't write code like this even in jest.
    Paul
  • HedbergHedberg Posts: 671
    a_riot42 wrote: »
    Please don't write code like this even in jest.
    Paul

    What about it upsets you so?
  • mpullinmpullin Posts: 949
    Hedberg wrote: »
    What about it upsets you so?
    A riot is generally upset.
  • integer test = 3
    
    define_function integer cubit(integer x)
    {
      x = x*x*x
    	return x
    }
    
    
    DEFINE_START
    
    y = cubit(test)
    

    y = 27
    test = 27

    Seems like reference to me.
  • HedbergHedberg Posts: 671
    mpullin wrote: »
    A riot is generally upset.

    The only thing that I can figure is that 42**3 is a pretty big number to try to store as an integer which seems to me to be a clever and mildly amusing joke.

    I've got to go now -- I'm memorizing multiplication tables in hex.
  • viningvining Posts: 4,368
    jweather:
    The only reason I would use it is for a function that needs to return multiple values, in which case the modified parameters would be designated as "out" parameters.
    Although I knew that is passes by reference it never dawn on me that I could use it to my advantage like this. When ever I needed more than one value modified in a function I always made those other values global to do so.

    I ran some tests just to help it soak into my brain:

    Code triggered every second.
     
    DEFINE_FUNCTION INTEGER fnTest_Incr_Values(INTEGER iValue, INTEGER iX, INTEGER iY, INTEGER iI)
    
         {
         iValue++ ;
         iX = iValue ;
         iY = iValue ;
         iI = iValue ;
         
         RETURN iValue ;
         }
    
    DEFINE_FUNCTION fnTestPassByRef()
    
         {
         STACK_VAR INTEGER nX ;
         STACK_VAR INTEGER nY ;
         STACK_VAR INTEGER nI ;
         LOCAL_VAR INTEGER nValue ;
         
         nValue ++ ;
         SEND_STRING dvMaster,"'Funtcion Ran, nValue = ',itoa(nValue),'.'" ;
         if(fnTest_Incr_Values(nValue,nX,nY,nI) < 10)
    	  {
    	  SEND_STRING dvMaster,"'Passed by Reference, X = ',itoa(nX),'.'" ;
    	  SEND_STRING dvMaster,"'Passed by Reference, Y = ',itoa(nY),'.'" ;
    	  SEND_STRING dvMaster,"'Passed by Reference, I = ',itoa(nI),'.'" ;
    	  }
         else
    	  {
    	  nValue = 0 ;
    	  }
         
         RETURN ;
         }
    
    

    Note that these are stack vars being passed and being modified outside the scope of code that created them. That is unless at compile time the function is pulled into the code block that calls it??

    Result:
    Line     10 (14:26:27):: Funtcion Ran, nValue = 1.
    Line     11 (14:26:27):: Passed by Reference, X = 2.
    Line     12 (14:26:27):: Passed by Reference, Y = 2.
    Line     13 (14:26:27):: Passed by Reference, I = 2.
    Line     14 (14:26:28):: Funtcion Ran, nValue = 3.
    Line     15 (14:26:28):: Passed by Reference, X = 4.
    Line     16 (14:26:28):: Passed by Reference, Y = 4.
    Line     17 (14:26:28):: Passed by Reference, I = 4.
    Line     18 (14:26:29):: Funtcion Ran, nValue = 5.
    Line     19 (14:26:29):: Passed by Reference, X = 6.
    Line     20 (14:26:29):: Passed by Reference, Y = 6.
    Line     21 (14:26:29):: Passed by Reference, I = 6.
    Line     22 (14:26:30):: Funtcion Ran, nValue = 7.
    Line     23 (14:26:30):: Passed by Reference, X = 8.
    Line     24 (14:26:30):: Passed by Reference, Y = 8.
    Line     25 (14:26:30):: Passed by Reference, I = 8.
    Line     26 (14:26:31):: Funtcion Ran, nValue = 9.
    Line     27 (14:26:32):: Funtcion Ran, nValue = 1.
    Line     28 (14:26:32):: Passed by Reference, X = 2.
    Line     29 (14:26:32):: Passed by Reference, Y = 2.
    Line     30 (14:26:32):: Passed by Reference, I = 2.
    Line     31 (14:26:33):: Funtcion Ran, nValue = 3.
    Line     32 (14:26:33):: Passed by Reference, X = 4.
    Line     33 (14:26:33):: Passed by Reference, Y = 4.
    Line     34 (14:26:33):: Passed by Reference, I = 4.
    Line     35 (14:26:34):: Funtcion Ran, nValue = 5.
    Line     36 (14:26:34):: Passed by Reference, X = 6.
    Line     37 (14:26:34):: Passed by Reference, Y = 6.
    Line     38 (14:26:34):: Passed by Reference, I = 6.
    Line     39 (14:26:35):: Funtcion Ran, nValue = 7.
    Line     40 (14:26:35):: Passed by Reference, X = 8.
    Line     41 (14:26:35):: Passed by Reference, Y = 8.
    Line     42 (14:26:35):: Passed by Reference, I = 8.
    Line     43 (14:26:36):: Funtcion Ran, nValue = 9.
    
  • a_riot42a_riot42 Posts: 1,624
    jweather wrote: »
    DEFINE_FUNCTION squareAndCube(INTEGER x, INTEGER outSquare, INTEGER outCube) {
      outSquare = x*x
      outCube = x*x*x
    }
    
    [...]
    INTEGER returnSquare, returnCube
    
    squareAndCube(42, returnSquare, returnCube)
    

    I was actually joking, but this breaks a few programming rules and I would hate to see it end up in someone's code by accident as it often does. You shouldn't modify outSquare, outCube but rather treat them as read only and return a stack_var. Plus a power() function is probably more appropriate that will calculate any number raised to any exponent. I realize you were demonstrating how to modify two variables in one function call, but generally speaking if you find yourself in the position of needing to do this, there is likely a flaw in the design somewhere, just like gotos.
    Paul
  • jweatherjweather Posts: 320
    jweather wrote: »
    I tend to avoid modifying parameters since it makes it hard to see what's happening. The only reason I would use it is for a function that needs to return multiple values, in which case the modified parameters would be designated as "out" parameters. Otherwise the function can just return the single value that it produces.
    a_riot wrote:
    I was actually joking, but this breaks a few programming rules and I would hate to see it end up in someone's code by accident as it often does. You shouldn't modify outSquare, outCube but rather treat them as read only and return a stack_var.

    I agree completely, although perhaps I didn't state it strongly enough: this isn't really a good idea. Odds are good that you will NEVER find an appropriate use for this code in AMX programming. If you think you have, try to refactor it into two or more functions instead. A little code duplication is better than something that will confuse other programmers maintaining your code (or yourself in 3 months, which is basically the same thing) However, it is possible, and you may see it in another programmer's code, and you should understand it for the sake of completeness.

    Jeremy
  • Spire_JeffSpire_Jeff Posts: 1,917
    Just looking for an opinion here. Would a function like this upset the programming gods?
    define_function fnGetIrDevice(integer nIndex, dev IrDevReturn){
       switch(nIndex){
         case 1:
           IrDevReturn = dvIrCable1;
         case 2:
           IrDevReturn = dvIrCable2;
         case 3: 
           IrDevReturn = dvTuner;
         case 4: 
           IrDevReturn = dvSat1;
         case 5: 
           IrDevReturn = dvSat2;
       }
    }
    
    button_event[dvTp,dvBtn]{
       push:{
         stack_var dev dvIrDevice;
         fnGetIrDevice(get_last(dvTp),dvIrDevice);
         send_command dvIrDevice,"'SP',button.input.channel";
      }
    }
    

    This function would fit into some code I have fairly easily, but I am also tossing around the idea of just creating an array of the devs and just returning the index pointer to the array. The reason I am considering the function listed is because I think it would be easier for someone else to make changes to the code and this code is being written for others to use it as well as myself.

    Jeff
  • Spire_Jeff wrote: »
    Just looking for an opinion here. Would a function like this upset the programming gods?
    define_function fnGetIrDevice(integer nIndex, dev IrDevReturn){
       switch(nIndex){
         case 1:
           IrDevReturn = dvIrCable1;
         case 2:
           IrDevReturn = dvIrCable2;
         case 3: 
           IrDevReturn = dvTuner;
         case 4: 
           IrDevReturn = dvSat1;
         case 5: 
           IrDevReturn = dvSat2;
       }
    }
    
    button_event[dvTp,dvBtn]{
       push:{
         stack_var dev dvIrDevice;
         fnGetIrDevice(get_last(dvTp),dvIrDevice);
         send_command dvIrDevice,"'SP',button.input.channel";
      }
    }
    

    This function would fit into some code I have fairly easily, but I am also tossing around the idea of just creating an array of the devs and just returning the index pointer to the array. The reason I am considering the function listed is because I think it would be easier for someone else to make changes to the code and this code is being written for others to use it as well as myself.

    Jeff

    The code below follows the holy programming proclamations set forth by the gods. In your case you don't need to modify a variable passed by reference. All you have to do is return the correct device. This is how standard functions work in other programming languages. C++ will allow you to pass variables by reference and modify them but it is considered sloppy and likely an indicator of poor design.

    If you are troubleshooting a block of code it can get very complicated if you have to jump around and look for functions that may or my not modify your variables.
    define_function DEV fnGetIrDevice(integer nIndex){
       switch(nIndex){
         case 1:
           RETURN dvIrCable1;
         case 2:
           RETURN dvIrCable2;
         case 3: 
           RETURN dvTuner;
         case 4: 
           RETURN dvSat1;
         case 5: 
           RETURN dvSat2;
       }
    }
    
    button_event[dvTp,dvBtn]{
       push:{
         stack_var dev dvIrDevice;
         dvIrDevice = fnGetIrDevice(get_last(dvTp));    // Assign the return value to dvIrDevice
         send_command dvIrDevice,"'SP',button.input.channel";
      }
    
  • Spire_JeffSpire_Jeff Posts: 1,917
    Not sure that I tried returning a DEV, but I thought that you could only return intrinsic values. Since DEV is a structure comprised of 3 integers, I don't think it will work. I will give it a try tho just to be sure.

    Jeff
  • Joe HebertJoe Hebert Posts: 2,159
    Bigsquatch wrote: »
    define_function DEV fnGetIrDevice(integer nIndex){
    
    A function can't return a DEV.
  • a_riot42a_riot42 Posts: 1,624
    Bigsquatch wrote: »
    define_function DEV fnGetIrDevice(integer nIndex){
       switch(nIndex){
         case 1:
           RETURN dvIrCable1;
         case 2:
           RETURN dvIrCable2;
         case 3: 
           RETURN dvTuner;
         case 4: 
           RETURN dvSat1;
         case 5: 
           RETURN dvSat2;
       }
    }
    
    button_event[dvTp,dvBtn]{
       push:{
         stack_var dev dvIrDevice;
         dvIrDevice = fnGetIrDevice(get_last(dvTp));    // Assign the return value to dvIrDevice
         send_command dvIrDevice,"'SP',button.input.channel";
      }
    

    I would question the need for a stack_var, then a function call and then a possibly long switch case depending on the number of devices, when you already have the index from get_last you need to send the code to the device in question while it sits waiting patiently in an array.
    Paul
  • Didn't think about not being able to return a DEV.

    My point was just to show the "proper" way to write functions.

    I agree that using a function in Jeff's example is not the best method to accomplish what he wants to do, but here is a version of the function that will work.
    define_function INTEGER fnGetIrDevice(integer nIndex){
       switch(nIndex){
         case 1:
           RETURN dvIrCable1.port;
         case 2:
           RETURN dvIrCable2.port;
         case 3: 
           RETURN dvTuner.port;
         case 4: 
           RETURN dvSat1.port;
         case 5: 
           RETURN dvSat2.port;
       }
    }
    
    button_event[dvTp,dvBtn]{
       push:{
         stack_var INTEGER dvIrDevicePort;
         dvIrDevicePort = fnGetIrDevice(get_last(dvTp));    // Assign the return value to dvIrDevice
         send_command 5001:dvIrDevicePort:1,"'SP',button.input.channel";
      }
    

    But don't use this block of code, it's nasty.
  • Spire_JeffSpire_Jeff Posts: 1,917
    The problem with doing it that way is that I would need to do a function for .Number, .Port, and .System. This may be the "correct" way to accomplish this, but the implementation seems more obfuscated than doing it the way it currently is. I guess I will just have to live with either the wrong way, or the way it is for now.

    Jeff
  • a_riot42a_riot42 Posts: 1,624
    Spire_Jeff wrote: »
    The problem with doing it that way is that I would need to do a function for .Number, .Port, and .System. This may be the "correct" way to accomplish this, but the implementation seems more obfuscated than doing it the way it currently is. I guess I will just have to live with either the wrong way, or the way it is for now.

    Jeff

    You don't need a function like that. If you have the index of the UI and the button that triggered the event then all you need to do is reference arrays to send a command to the device you need.
    Paul
Sign In or Register to comment.