&k1S CALLING "C" BINARIES FROM TECH BASIC&k0S GVG / 12 February 1986 / V1.1 + HPUX Technical BASIC allows you to call "C" subroutines from BASIC and pass parameters to them, using the CALLBIN statement. This note outlines the syntax of CALLBIN, explains how to write a "C" subroutine to be used through CALLBIN, explains how to compile the "C" subroutine and connect it to a BASIC program, and illustrates the whole process with an example. &dD&k3SThe CALLBIN Statement&k0S&d@ + The CALLBIN statement has the syntax: [CALLBIN]-+->["]->[subroutine name]->["]-+-+-------------------------+-> | ^ | ^ | | | | +----->[string expression]-----+ +->[(]->[parameters]->[)]-+ The "string expression" is any expression that returns a string as a value - in this case, a string that gives the name of a "C" subroutine. The syntax of the "parameters" is as follows: +--------------------[,]<----------------------+ | | V | -+-+->[variable name]--+--------------------+-+-+-> <--- passed by address | | ^ ^ | | | | | +->[(]-+------+->[)]-+ | | | ^ | | | | | | +->[,]-+ | | | | | +---->[(]->[simple variable name]->[)]---->+ <-+ | | | | | | +------------>[array element]------------->+ <-+ | | | | | | +-------------->[constant]---------------->+ <-+- passed by value | | | | | | +---------->["]->[literal]->["]----------->+ <-+ | | | | | | +-->[arithmetic / relational expression]---+ <-+ + Let me review some facts of life about BASIC variable types before I go on and give details about the preceding syntax diagram. BASIC uses the following variable types: + Simple numeric variables - either REAL, SHORT, or INTEGER; default is REAL. (REAL types are 64-bit floating-point numbers; SHORT types are 32-bit floating-point numbers; INTEGER types are 32-bit signed integers.) + Numeric array variables - again, either REAL, SHORT, or INTEGER; default is REAL. Such arrays can have one or two dimensions. The lower bound of each dimension is "0" by default, although it can be set to be "1"; IT MUST BE LEFT ZERO IF YOU WANT TO USE THE ARRAY AS A PARAMETER FOR A "C" SUBROUTINE. The maximum upper bound of each dimension is 65,530. + Simple string variables - essentially an array of ASCII characters (bytes), up to 65,530 characters long (although the default size is 18 characters). (String variables always have a "$" at the end of their name; numeric variables never do. String constants - like, say, "balderdash", are actually string arrays and can be accessed as such.) A string contained in a string variable must be ended by a "null" character if you want to pass it to a "C" subroutine as a parameter. This can be done in several ways; you could, for example, use the the string "string~0", or the string expression "hello" & CHR$(0). (There are some other peculiarities to using strings as parameters to "C" subroutines that I'll get to in a moment.) + Arrays of string variables - subject to the same restrictions on dimensions, bounds, and size as numeric arrays. BASIC also deals with other objects, like: + Constants - either numeric or string (like, say, "Floyd"). + Literals - String expressions, including string variables, string constants, or string functions (like CHR$), or combinations of all three. + Arithmetic expressions - like, say, "4*somevar". + Relational expressions - like, say, "somevar<9". A relational expression evaluates to "1" if true and to "O" if false. + The syntax diagram indicates that you can send the address of any of these variables to a "C" subroutine simply by giving the name of the variable - WITHOUT parentheses - as a parameter to CALLBIN. For example, if you want to pass the address of a numeric variable, say COUNT, and a string variable, say NAME$ to a "C" subroutine, you would simply enter: CALLBIN "Csub" (COUNT,NAME$); But why send the address? (This is called "call by address", by the way.) Because the alternative is to just the send the VALUE contained in the variable to the "C" subroutine - which means that the subroutine CAN'T CHANGE THE VARIABLE BECAUSE ALL IT HAS IS ITS CONTENTS! (This is called "call by value".) Sending the address gives the subroutine access to the variable itself. THIS IS THE ONLY WAY THE C SUBROUTINE CAN PASS VALUES BACK TO THE CALLING ROUTINE. Sending the addresses of arrays is just as easy. Suppose you have an array named SOMEARRAY. Then you would pass the array as SOMEARRAY(). If that array was a two-dimensional array, then you would pass its address as SOMEARRAY(,). The only place where sending addresses to the "C" subroutine gets really tricky is in sending string variables (that is, arrays of characters). The problem comes up because BASIC and "C" have entirely different ways of figuring out how many characters are in a string variable. (Lest there be confusion, the number of characters in a string is NOT the same as the length of the string variable. The length of the string variable is fixed; the number of characters in it may vary from none to the length of the string.) "C" expects all strings to have a null character at the end (which is why, as I mentioned earlier, you have to make sure you put a null at the end of the string); the only way a "C" subroutine can figure out how many characters are in the string is to count the number of characters before the null. BASIC, on the other hand, keeps the number of characters in a string variable in another variable that a "C" subroutine can't get at. So what's the problem? The problem is that if the "C" subroutine is passed the address of a string variable, and then loads a string LONGER than the one already in the variable, BASIC will not "see" any of the characters in the new string that exceed the number of characters in the old. You have to do some pushups to take care of this problem. There's two steps: 1: Pad the string variable with blanks all the way to its end; easy to do, all you need is the command: somestring$[LEN(somestring$)+1]=" " (It doesn't seem obvious that this should work, but one of our support engineers checked it out interactively as follows: DIM Z$[15] LET Z$="hello" Z$ hello LEN(Z$) 5 LET Z$[LEN(Z$)+1]=" " Z$ hello LEN(Z$) 15 Z$ hello Piece of cake.) You have to take some precautions, however, since trying to do this to a string variable that's already full will cause an error. (Use the LEN command.) You can now pass the address of the string to the "C" subroutine as a parameter. 2: Once you get the string back, you have to figure out how many characters the string has passed back. There's two ways to do this: First, you could pass the address of a simple variable to the "C" subroutine and have it return the number of characters in the string; then you would assign the number of characters to the string as: somestring$ = somestring$[1,howlong] - where "howlong" is the simple variable passed to the "C" subroutine. Second, you could search the string for the null character at its end and use that to assign the length at follows: nullpos = POS(somestring$,CHR$(0)) somestring$ = somestring[1,nullpos-1] (Enough, already! Let's move on.) + The syntax diagram also indicates that you can send the VALUE of a simple numeric variable simply by enclosing the variable name in parentheses; for example: CALLBIN "Csub" ((COUNT)); - sends the value of COUNT to a subroutine. You can also send the VALUE of a single array element by giving its name and array index: CALLBIN "Csub" (SOMEARRAY[1][6]); (There is, by the way, no convenient way to send the address of a single array element.) Finally, you can send constants - either numeric or string - or the results of arithmetic or relational expressions as values to "C" subroutines: CALLBIN "Csub" (27,"Floyd~0",4*COUNT, COUNT