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
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: where
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:
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.
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.
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; } }@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 am reluctant to use external variables. but I think I will have no way out. I need a function that returns a DEV.
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.
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.
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.
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.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,
Ultimately, there are limitations in the language and you have to work within those limitations.
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..
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.