Home AMX User Forum AMX Technical Discussion

Functions and Multi-Dimensional Arrays

Is it possible to return a multidimensional array from a function? Currently I get the following runtime error:
GetString - Error 1 Tk=0x0000
CopyString (Reference) - Error 2 S=0x0000 D=0x1033

Comments

  • ericmedleyericmedley Posts: 4,177
    please post the function and the array
  • PhreaKPhreaK Posts: 966
    ericmedley wrote: »
    please post the function and the array

    Sure. For example:
    DEFINE_FUNCTION CHAR[3][16] fnTest ()
    {
        LOCAL_VAR sReturn[3][16]
        
        sReturn[1] = 'test 1'
        sReturn[2] = 'test 2'
        sReturn[3] = 'test 3'
        
        RETURN sTest
    }
    

    Which is run as:
    sTest = fnTest()
    
    where
    DEFINE_VARIABLE
    
    CHAR sTest[3][16]
    
  • jjamesjjames Posts: 2,908
    Just curious, what happens when you remove the length out of the function's return declaration?

    DEFINE_FUNCTION CHAR[][] fnTest ()

    I'd compile except I got a strange error that pops up saying: "This special version of the NetLinx Compiler has now expired (Monday, June 01, 2009). Please contact AMX for further information." :eek:
  • AuserAuser Posts: 506
    From the NetLinx Keywords Help File:
    NOTE: The return type may only be one of the 8 intrinsic types. Strings, arrays, structures, classes and other user-defined types may not be returned.

    Clearly not accurate, but that'd be a no on returning multidimensional arrays. Just pass a variable as a parameter (ie. by reference) and put the return value in that variable.
  • PhreaKPhreaK Posts: 966
    jjames wrote: »
    Just curious, what happens when you remove the length out of the function's return declaration?

    DEFINE_FUNCTION CHAR[][] fnTest ()

    That won't compile. If you are returning an array it has to be bounded.

    I've contacted AMX here in australia to see if it's possible. They've had a quick play and weren't able to give a definative answer as to weather it can, or should, work, however they're getting in touch with their american couterparts for some info. When I hear back I'll post the results.
  • GSLogicGSLogic Posts: 562
    PhreaK wrote: »
    Is it possible to return a multidimensional array from a function? Currently I get the following runtime error:
    GetString - Error 1 Tk=0x0000
    CopyString (Reference) - Error 2 S=0x0000 D=0x1033
    

    Instead of using a RETURN to handle the structure/array, just create another variable to hold the content you want to copy. I do it all the time with very large multi-dimensional structures and arrays. You might have to rethink some of your code design, but it will works.

    (***********************************************************)
    DEFINE_DEVICE
      dvTP = 10001:1:0;
    
    
    (***********************************************************)
    DEFINE_TYPE
      structure	_test
      {
    	integer t1
    	integer t2
    	integer t3
      }
    
    (***********************************************************)
    DEFINE_VARIABLE					 
      _test test
      _test testCopy
      
      integer testArray[3];
      integer testArrayCopy[3];
    
    
    (***********************************************************)
    DEFINE_EVENT
    //###########################################################
    BUTTON_EVENT[dvTp,1]   // load data
    {
      PUSH:
      {
    	test.t1 = 1
    	test.t2 = 2
    	test.t3 = 3
    
    	testArray[1] = 1;
    	testArray[2] = 2;
    	testArray[3] = 3;
      }
    }
    
    
    BUTTON_EVENT[dvTp,2]   //copy data
    {
      PUSH:
      {
    	testCopy = test;
    	testArrayCopy = testArray;
      }
    }
    
  • PhreaKPhreaK Posts: 966
    Unfortunately I'm not just using it as an data handler to pass arrays / structures around the place, that function was just a stripped down example that would throw the error.

    @Auser manipulating variables passed as parameters goes against all rules of nice coding. It will work but I try to keep everything as logical and standardised as possible in all my code as it tends to prevent a lot of issues in the future.
  • I'm with same problem.

    I am reluctant to use external variables. but I think I will have no way out. I need a function that returns a DEV.
  • DHawthorneDHawthorne Posts: 4,584
    I've found it impossible to not use external variables once in a while. If NetLinx was fully OO, and you could define methods, or declare public functions to use outside an object, then I would never do so. But it's not the hand we have been dealt.
  • I started working with external variables, but not like the result and remodeled the form of Work. This gives me a bit more code. But logic is more enjoyable.
  • AuserAuser Posts: 506
    PhreaK wrote: »
    @Auser manipulating variables passed as parameters goes against all rules of nice coding. It will work but I try to keep everything as logical and standardised as possible in all my code as it tends to prevent a lot of issues in the future.

    I must've missed this one back in the day. I'm not saying you're wrong, but if you've only got coconuts to work with, you can only build stuff out of coconuts. Think of the array variable you're passing in as a pointer to itself if it'll make you feel better :P

    I know there are other ways to do it, but most of the ways I've thought of result in code which is more obfuscated and prone to errors and maintenance issues.
  • truetrue Posts: 307
    He isn't wrong. Manipulating function parameters passed-by-reference should only be done in rare cases (basically never). This is how it works in other languages. Sadly, due to limited return types, we have to do this now and again.

    Another bug with passing a multidimensional array is that array/string_max_length (and possibly array/string_length too, can't remember) return 0 instead of the proper value when used in the function you passed the array to (at least with unbounded arrays). Compiler bugs suck.
  • viningvining Posts: 4,368
    You could always use string_to_var and var_to_string to return complex arrays and structures if you wanted, right? I don't blieve I've ever done this in a return of a function but you can pass structures to modules using this method so it stands to reason you could pass a structure to a function, covert it to a string and pass back the string and then convert it back to a structure if you wanted.
  • It is possible to do in many ways, even with limitations.
    The hard part is creating a logical structure and change everything because of it.
    I had to work a little more so that everything remains flexible and dynamic as I had thought.

    At the end are going alright.
  • viningvining Posts: 4,368
    Just so happens that today I ended up needing to pass a struct to a function and then return it using the var_to_string. The real intent was not just to return the structure from the function using var_to_string but ultimately send it from this .axi which gets pulled into modules back to the main code where another program processes and sends it to the office server.

    If it were to stay in the module I would have no problem ccreating a global struct or even changing values by reference. I've never been taught the "correct" rules and never was very good at following rules anyway.
    DEFINE_TYPE //MESSAGE QUEUE
    
    STRUCTURE _sSitrepMsg //match the VAV_SitRep_Client structure
         
         {
         CHAR cTask[255] ;
         CHAR cFrom[255] ;
         CHAR cURL[255] ;
         CHAR cUser[128] ;
         CHAR cPass[128] ;
         CHAR cSubj[255] ;
         CHAR cBody[2048] ;
         CHAR cFile[255] ;
         CHAR cTime[8] ;
         CHAR cLDate[10] ;
         }
         
    DEFINE_VARIABLE
    
    VOLATILE INTEGER nSitRepModClient_DeBug = 0 ;
    //#WARN 'nSitRepModClient_DeBug = 1'
         
    DEFINE_FUNCTION fnSitrepModClient_DeBug(CHAR iStr[])
    
         {
         if(nSitRepModClient_DeBug)
    	  {
    	  STACK_VAR CHAR cCopyStr[1024] ;
    	  STACK_VAR INTEGER nLineCount ;
    	  
    	  cCopyStr = iStr ;
    	  
    	  nLineCount ++ ;
    	  WHILE(length_string(cCopyStr) > 80)
    	       {
    	       SEND_STRING 0,"'SITREP_MODClient.axi (',itoa(nLineCount),') DEBUG: ',get_buffer_string(cCopyStr,80)" ;
    	       nLineCount ++
    	       }
    	  if(length_string(cCopyStr))
    	       {
    	       SEND_STRING 0,"'SITREP_MODClient.axi (',itoa(nLineCount),') DEBUG: ',cCopyStr" ;
    	       }
    	  }
         
         RETURN ;
         } 
         
    DEFINE_FUNCTION CHAR[8192] fnSitrepMod_VarToStr(_sSitrepMsg iSitrepMsg)
         
         {
         STACK_VAR LONG nPos ;
         STACK_VAR SINTEGER nResult;
         STACK_VAR CHAR cEncoded[8192] ;
         
         nPos = 1 ;
         nResult = VARIABLE_TO_STRING(iSitrepMsg,cEncoded,nPos) ;
         if(nResult == 0)// Success
    	  {
    	  fnSitrepModClient_DeBug("'RX_Msg, Rx VarToStr encode OK! ',itoa(length_string(cEncoded)),' byte str :DEBUG<',ITOA(__LINE__),'>'") ;
    	  RETURN cEncoded ;
    	  }
         else// Failure
    	  {
    	  fnSitrepModClient_DeBug("'RX_Msg, Rx Variable To String encode ERR# (',itoa(nResult),') :DEBUG<',ITOA(__LINE__),'>'") ;
    	  RETURN '' ;
    	  }
         }     
         
    DEFINE_FUNCTION SLONG fnSitrepQueMSG(CHAR iTask[],CHAR iFrom[],CHAR iURL[],CHAR iUser[],CHAR iPass[],CHAR iSubject[],CHAR iBody[],CHAR iFile[])
    
         {
         STACK_VAR _sSitrepMsg sSitrepMsg ;
         STACK_VAR CHAR cReturn[8192] ;
         
         sSitrepMsg.cTask  	= iTask ;
         sSitrepMsg.cFrom  	= iFrom ;
         sSitrepMsg.cURL   	= iURL ;
         sSitrepMsg.cUser 	= iUser ;
         sSitrepMsg.cPass  	= iPass ;
         sSitrepMsg.cSubj  	= iSubject ;
         sSitrepMsg.cBody  	= iBody ;
         sSitrepMsg.cFile	= iFile ;
         sSitrepMsg.cTime  	= '' ; //TIME ; not used so why bother//gets set on the other side
         sSitrepMsg.cLDate 	= '' ; //LDATE ; 
    
         cReturn = fnSitrepMod_VarToStr(sSitrepMsg) ;
         if(length_string(cReturn))
    	  {
    	  SEND_STRING vSitrep_Client,"1,cReturn" ;
    	  }
         }
    
    Instead of sending it out as a string I could just as easily reloaded the initial struct or new structure with the returned value using the string_to_var which is what I do in the main code. It just isn't worth it IMHO if you don't have to transport it out of a module.
  • AuserAuser Posts: 506
    true wrote: »
    He isn't wrong. Manipulating function parameters passed-by-reference should only be done in rare cases (basically never). This is how it works in other languages. Sadly, due to limited return types, we have to do this now and again.

    We seem to be in violent agreement on this one. I never suggested he was wrong (note the "I'm not saying you're wrong" bit in the post you replied to) and never said it was a nice way to do things in other languages, I was just miffed that I gave the answer to the question posed in the OP citing a literary reference and a suggested solution and the original poster suggested that he was above doing such things but didn't offer an alternate, more elegant solution.

    As I said,
    Auser wrote: »
    [...] if you've only got coconuts to work with, you can only build stuff out of coconuts.

    Ultimately, there are limitations in the language and you have to work within those limitations.

    true wrote: »
    Another bug with passing a multidimensional array is that array/string_max_length (and possibly array/string_length too, can't remember) return 0 instead of the proper value when used in the function you passed the array to (at least with unbounded arrays). Compiler bugs suck.

    FWIW, I have never come across this.

    So given the comments to the effect that it is universally bad practice to return results from a function in variables passed to the function by reference (naughty AMX engineers and their REMOVE_STRING function), here's a challenge for everyone. I'm always happy to learn new things and look forward to seeing what you come up with.

    The rules are simple:

    - Create a function that adds a string to a list of strings stored in a 2D char array
    - The function must be able to deal with arrays of any realistic size
    - Comments should be limited to Netlinx, we're not dealing with Duet (or other languages) here
    - The function may return it's result in any way
    - The function must be at least as logical/elegant/efficient as the following:
    // char StringCollection_Add(char cCollection[][], char cNewItem[])
    // Adds a string to an array of strings. Returns true if successful, false otherwise.
    
    define_function char StringCollection_Add(char cCollection[][], char cNewItem[])
    {
      stack_var  integer  nCollectionSize
      nCollectionSize = length_array(cCollection)
      if
      (
        (nCollectionSize < max_length_array(cCollection)) &&
        (length_array(cNewItem) <= max_length_array(cCollection[1]))
      )
      {
        nCollectionSize++
        cCollection[nCollectionSize] = cNewItem
        set_length_array(cCollection, nCollectionSize)
        return TRUE
      }
      else
      {
        return FALSE
      }
    }
    

    Vining has already mentioned a method (ie. using var_to_string/string_to_var) which I'll have to discount based on grounds of efficiency in this particular scenario. (There's nothing wrong with the method in other places - I've been known to use var_to_xml/xml_to_var to pass complex data sets between disjoint processing systems (across module/machine boundaries) on rare occasions..
  • PhreaKPhreaK Posts: 966
    Auser wrote: »
    I was just miffed that I gave the answer to the question posed in the OP citing a literary reference and a suggested solution and the original poster suggested that he was above doing such things but didn't offer an alternate, more elegant solution.

    Apologies if it came across like that - upon reading it back it does sound rather douchebag-esque. I didn't offer an alternative because I didn't have one, hence the post. I was just trying to keep the discussion going.

    Where there is no way around it I've been using the same technique you suggested, and just ensuring that the function name, parameter names and comments make it clear that the variable passed will be manipulated.
Sign In or Register to comment.