Home AMX User Forum AMX Technical Discussion

Odd ATOI behaviour- why??

Found this behavior when using ATOI and GET_BUFFER_CHAR together:
ACTIVE(FIND_STRING (CLIENTSTRING, "'RLS# 1 8 1'",1)): //RETURN FROM VANTAGE PARTY BUTTON
     {
      SEND_STRING 0,"'RLS# 1 8 1 FOUND'"
      REMOVE_STRING (CLIENTSTRING, "'RLS# 1 8 1 '",1)
      SEND_STRING 0,"'VANTAGE CLIENTSTRING:',CLIENTSTRING"
      nBTNLED = ATOI(GET_BUFFER_CHAR(CLIENTSTRING))
      SEND_STRING 0,"'nBTNLED: ',nBTNLED"
       IF (nBTNLED = 0)
           {
           OFF [devTPLIGHTS,30] //PARTY BUTTON
           }
       IF (nBTNLED = 1)
           {
           ON [devTPLIGHTS,30]    //PARTY BUTTON
           }
     CLEAR_BUFFER CLIENTSTRING
     }

gives these results:
Line 330 (10:48:37):: VANTAGE CLIENTSTRING: RLS# 1 8 1 1$0D$0A
Line 331 (10:48:37):: VANTAGE BUFFER AFTER REMOVE: SW 1 8 1 0$0D$0ARSW 1 8 1 5 $0D$0A
Line 332 (10:48:37):: RLS# 1 8 1 FOUND
Line 333 (10:48:37):: VANTAGE CLIENTSTRING:1$0D$0A
Line 334 (10:48:37):: CIpLibrary::Atoi - Error
Line 335 (10:48:37):: Library Call error on Line 3649
Line 336 (10:48:37):: nBTNLED:

(nBTNLED is a LOCAL_VAR SINTEGER)


Adding double quotes fixes the problem:
	nBTNLED = ATOI("GET_BUFFER_CHAR(CLIENTSTRING)")


As does the AMX recommended method of removing the GET_BUFFER_CHAR:
	nBTNLED = ATOI(CLIENTSTRING)

Why? What causes this and what is the rhyme/reason to how and why this works this way? Is GET_BUFFER_CHAR broken, or is ATOI broken, or, is there some anomaly when using them together? I have other chunks of code that use each of these and in looking through my code, there are places where I have had to add double quotes and other places where it works without them.

For instance, this works without the double quotes:
	nOUTPUT = ATOI(GET_BUFFER_STRING(OUTPUT,(LENGTH_STRING(OUTPUT)-1)))
But this I had to add double quotes to:
	nAPZONE = ATOI("LEFT_STRING(cAPBUTTONSTRING,LENGTH_STRING(cAPBUTTONSTRING)-1)")

Is there a best practices lesson here?

Comments

  • John NagyJohn Nagy Posts: 1,742
    Not exactly what you have but perhaps related, at least thematically... We've been seeing compile errors traced back to an ITOA command that isn't being recognized in the composing window as a command. When this happens, randomly, go back to the line of code and ITOA is in plain text. Remove any letter and retype, THEN it BOLDS and is recognized as the command. Then the compile works fine and the code is fine. Then it happens again, in a different part of the code another time, a part we didn't even open, just happens at compile time, from the same source. We see this in about 1 out of ~40 compiles. Sometimes you can't tell until something doesn't run right, it will compile, but a debug shows it isn't understanding ITOA, We've reported it,
  • My guess is that ATOI is looking for a character array, not a single character. When you use get_buffer_char as the argument to ATOI, it doesn't match what the function is looking for as an argument, and so it throws an error. When you put the double quotes around the get_buffer_char, it turns it into a character array, albeit of length 1, that matches what the function is looking for. The same thing is true when you use get_buffer_string with a length of 1.

    In fact, if you look at netlinx.axi, the ATOI function is defined as needing an argument of char a[]. You can get the same issue if you create your own function that takes an array of anything, even integers, as an argument and give it a dimensionless variable when you call the function.
    DEFINE_VARIABLE
    integer nTestArray[3]
    integer nTest
    DEFINE_FUNCTION fnTest(integer n[],integer o){
         //do some stuff
    }
    
    button_event[dvTP,0] {
         push: {
              fnTest(nTest,nTestArray)
         }
    }
    
    

    If you compile the above code, the compiler will throw an error saying that there is a dimension mismatch, [1] vs. [0] and a type mismatch in call for parameter N. It will throw the same error for parameter 0 if you fix the call for the function to fnTest(nTestArray,nTestArray).

    The same thing is going on with ATOI when you use get_buffer_char as the argument. It's return has a dimension of 0, so it doesn't fit the requirements for the function definition. It would be nice if the compiler would throw the error, rather than catching it at runtime, but that's the way it is.

    Hope that helps.
  • ppdkppdk Posts: 31
    Since the CLIENTSTRING will left over with a number in it (after the remove_string) you can simply call ATOI passing it the CLIENTSTRING as a hole without get any char.
    nBTNLED = ATOI(CLIENTSTRING)
    

    On the other hand, if you expect the CLIENTSTRING to return more than one char number (e.g "12") then you could also use:
    nBTNLED = ATOI(CLIENTSTRING[1])
    
    to extract the first char and convert it to integer

    Kostas

    P.S.
    Performing a numerous compilations in the following lines NEVER gave me an error. My NXS is v3.3.1.525 and NX Compiler v2.5.2.20
    (nVAR1 is declared as an INTEGER and cTest as: CHAR cTest[6] both VOLATILE in DEFINE_VARIABLE section)
    nVAR1 = ATOI(cTest[1])
    nVAR1 = ATOI(GET_BUFFER_CHAR(cTest))
    nVAR1 = ATOI("GET_BUFFER_CHAR(cTest)")
    nVAR1 = ATOI(GET_BUFFER_CHAR(cTest[1]))
    nVAR1 = ATOI("GET_BUFFER_CHAR(cTest[1])")
    
  • ericmedleyericmedley Posts: 4,177
    Is the '1' in the string return a decimal one (as in $01) or is it an ASCII 1 (as in a Hex $31). If its the former, then you don't need to use atoi. I've seen plenty of protocol like this.
  • viningvining Posts: 4,368
    Using get_buffer_char w/o the double quote shouldn't cause a compile error but it should throw a runtime error when the code actually runs.

    As Andrew mentioned atoi is expecting a string not a char so the double quotes does affectively create a string with a length of 1. You could also use atoi(get_buffer_string(cStr,1)) or as as previously stated atoi(cStr[1]) should also work.
  • ericmedley wrote: »
    Is the '1' in the string return a decimal one (as in $01) or is it an ASCII 1 (as in a Hex $31). If its the former, then you don't need to use atoi. I've seen plenty of protocol like this.

    ASCII as in $31.
  • So if I am understanding correctly,
    ATOI is expecting a string or an array to be passed, a single CHAR variable will throw this error during runtime, but will compile.
    In that case, would this cause the same error?
    LOCAL_VAR CHAR cCHAR_VAR 
    LOCAL_VAR INTEGER nINT_VAR
    
    cCHAR_VAR = '2'
    nINT_VAR = ATOI(cCHAR_VAR)
    

    I am not in front of a system to compile and test on.

    Is it a good rule of thumb to use the double quotes with ATOI in places where you may encounter a string length of 1? Are there any downsides to always using double quotes with ATOI expressions?
  • ppdk wrote: »
    P.S.
    Performing a numerous compilations in the following lines NEVER gave me an error. My NXS is v3.3.1.525 and NX Compiler v2.5.2.20
    (nVAR1 is declared as an INTEGER and cTest as: CHAR cTest[6] both VOLATILE in DEFINE_VARIABLE section)
    nVAR1 = ATOI(cTest[1])
    nVAR1 = ATOI(GET_BUFFER_CHAR(cTest))
    nVAR1 = ATOI("GET_BUFFER_CHAR(cTest)")
    nVAR1 = ATOI(GET_BUFFER_CHAR(cTest[1]))
    nVAR1 = ATOI("GET_BUFFER_CHAR(cTest[1])")
    

    What was the value of nVAR1 at runtime here? Did all formats return the same value to nVAR1?

    I am not seeing any compiler errors, just errors when the code runs and a var doesn't work the way that it should.
  • Yep
    PHSJason wrote: »
    So if I am understanding correctly,
    ATOI is expecting a string or an array to be passed, a single CHAR variable will throw this error during runtime, but will compile.
    In that case, would this cause the same error?
    LOCAL_VAR CHAR cCHAR_VAR 
    LOCAL_VAR INTEGER nINT_VAR
    
    cCHAR_VAR = '2'
    nINT_VAR = ATOI(cCHAR_VAR)
    

    I am not in front of a system to compile and test on.

    Is it a good rule of thumb to use the double quotes with ATOI in places where you may encounter a string length of 1? Are there any downsides to always using double quotes with ATOI expressions?

    Yes, this should throw the runtime error. The cCHAR_VAR is not a string, but a single character. If you had declared it like so:
    LOCAL_VAR CHAR cCHAR_VAR[1]
    LOCAL_VAR INTEGER nINT_VAR
    
    cCHAR_VAR = '2'
    nINT_VAR = ATOI(cCHAR_VAR)
    

    it would work and the value of nINT_VAR would be 2.
  • ppdkppdk Posts: 31
    OK. I did compiled and run the code in an NI-700.
    The initial value of nVAR1 is 0 and the initial value of cTest is '123456'.
    Here are the results of the runtime behavior of the source code:

    All the following statements throw a runtime Error:
    nVAR1 = ATOI(cTest[1])
    nVAR1 = ATOI(GET_BUFFER_CHAR(cTest))
    nVAR1 = ATOI(GET_BUFFER_CHAR(cTest[1]))
    nVAR1 = ATOI("GET_BUFFER_CHAR(cTest[1])")
    


    and the following statements work as expected:
    nVAR1 = ATOI("cTest[1]")
    nVAR1 = ATOI("GET_BUFFER_CHAR(cTest)")
    

    Kostas
  • mpullinmpullin Posts: 949
    PHSJason wrote: »
    Is it a good rule of thumb to use the double quotes with ATOI in places where you may encounter a string length of 1? Are there any downsides to always using double quotes with ATOI expressions?
    Always use double quotes with the parameter to atoi(). There is no downside to using them. "Rule of thumb" implies there may be exceptions... and in this case, there are none.
  • mpullin wrote: »
    Always use double quotes with the parameter to atoi(). There is no downside to using them. "Rule of thumb" implies there may be exceptions... and in this case, there are none.


    Thank you for the clarification. Does this also apply to ITOA, HEXTOI, etc?
  • mpullinmpullin Posts: 949
    PHSJason wrote: »
    Thank you for the clarification. Does this also apply to ITOA, HEXTOI, etc?
    No to itoa() because the parameter is not a string, yes to hextoi().

    itoa(12) = '12'
    atoi("'12'") = 12
    hextoi("'12'") = 18
  • mpullin wrote: »
    No to itoa() because the parameter is not a string, yes to hextoi().

    itoa(12) = '12'
    atoi("'12'") = 12
    hextoi("'12'") = 18

    Also I would assume that any other conversion keyword like ATOL, ATOF, etc that uses a string as an input should also get double quotes.

    Thanks again, and yet it goes against what AMX tech told me. AMX TECH (after conferring with another tech agent) had me omit the double quotes in the first example and just pass the entire string variable to ATOI and let it 'ignore' the unprintable characters or $OD, and $OA.


    In a case where CLIENTSTRING = "'1',$0D, $0A",
    I was told to not use this:
    nBTNLED = ATOI("GET_BUFFER_CHAR(CLIENTSTRING)")

    and to instead use this:
    nBTNLED = ATOI(CLIENTSTRING)


    Both of these work, however.

    I prefer to use the top one, instead of relying on the system always properly ignoring the unprintable characters. but perhaps a better one would be:
    nBTNLED = ATOI("LEFT_STRING(CLIENTSTRING,1)")
    or
    nBTNLED = ATOI("GET_BUFFER_STRING(CLIENTSTRING,1)")
  • mpullinmpullin Posts: 949
    PHSJason wrote: »
    In a case where CLIENTSTRING = "'1',$0D, $0A",
    I was told to not use this:
    nBTNLED = ATOI("GET_BUFFER_CHAR(CLIENTSTRING)")

    and to instead use this:
    nBTNLED = ATOI(CLIENTSTRING)
    I'd use atoi("clientstring"); atoi() is pretty smart... if it sees something that looks like a number followed by other characters it will ignore the rest. The GET_BUFFER_CHAR is not necessary.
Sign In or Register to comment.