The CSUB prototyper saves time in the creation of CSUBs. It consists of CSUBs and BASIC functions that let you call CSUB routines from BASIC in their "native" language (FORTRAN, Pascal, C, or assembly). In order to call CSUB routines in this manner, prototyper functions convert actual parameter types of the BASIC calling routine to the formal parameter types of the CSUB routine being called.
Before continuing, you should review the chapter in this manual that pertains to your particular CSUB language.
The CSUB prototyper has two functions:
The following table gives a comparison between the procedural steps for creating a CSUB using the standard method and using the prototyper.
Step | Standard CSUB Procedure | Prototyper CSUB Procedure |
---|---|---|
1 | Create the BASIC program that is to call the CSUB(s). | Same. |
2 | Exit BASIC and enter the HP-UX environment. | Same. |
3 | Select an editor (vi for example) and write a program that contains your CSUBs. This program can be written in FORTRAN, Pascal, C, or assembly language. Run the program and debug it until it works. | Same. |
4 | Provide the proper interface for the CSUB(s) by defining the formal parameters of the CSUB(s). | This step is not required. |
5 | Link the necessary libraries to your compiled CSUB to generate a CSUB object file. | Same. |
6 | Execute rmbbuildc | This step is not required. |
7 | Load the CSUB(s) by executing the LOADSUB command from the keyboard or the BASIC program that is calling the CSUB(s). | Load the CSUB(s) by specifying the CSUB object file to the appropriate prototyper CSUB. |
The table shows that the CSUB prototyper shortens the steps required to create a CSUB. This will save time, but you will need to have enough memory to hold the object code for an entire program that contains the CSUB(s).
The CSUB prototyper also opens up new possibilities for a BASIC application whose requirements for compiled language routines can only be determined at run-time. In this situation, you need not anticipate the usage requirement of the application by defining a CSUB interface for all of the possible routines that may be invoked. Instead, the selected routines may be called by name with the CSUB prototyper. By carefully restricting this method of accessing CSUBs to those routines that are not time critical, you can use this feature to give an application greater flexibility in the area of dynamic code loading without impairing its performance.
This section develops a complete example that illustrates all the major steps involved in using the CSUB prototyper. You may find it useful to refer to the section "Steps for Creating a CSUB" in chapter 2, 3, or 4 of this manual, depending on the language you are using to create your CSUB. In our example, we will use the C language.
The procedure for writing CSUB routines consists of five steps.
This BASIC program shows a typical session with the CSUB prototyper.
100 REAL A ! Real variable file descriptor.
110 REAL J
120 REAL R
130 DIM S$[30]
140 ! Load the CSUB prototyper library.
150 LOADSUB ALL FROM "CPR"
160 ! Load the object file containing the CSUB routines.
170 Cprload(A,"example")
180 ! Call procedure "sub1"
190 Cpr(FNInit(A,"_sub1"),FNI(50),FNL(J,1),FNS(S$,1))
200 PRINT "J is",J
210 PRINT "S$ is",S$
220 ! Call function "sub2" which returns a 64-bit floating
230 ! point value.
240 R=FNCpr64(FNInit(A,"_sub2"),FND(45))
250 PRINT "R is",R
260 ! Unload the object file.
270 Cprunload(A)
280 END
The output of this program is as follows:
J is 50
S$ is sub1
R is 90
This program illustrates the major operations provided by the CSUB prototyper:
A
.
CPR
.
_sub1
and _sub2
) into memory. The
prototyper command called Cprload
is used to load the
object files. Note that the REAL variable A
is assigned
as the file descriptor for the object file called
example
. File descriptor A
will be
used when you reference the compiled routines _sub1
and
_sub2
.
Cpr
. Note that this execute CSUB is type
void, which is the same type as the compiled routine called
_sub1
. A list of execute CSUBs and functions
can be found in the table entitled "Mapping Between BASIC Return Value Type
and Prototyper execute CSUBs or Functions." The parameters for this
execute CSUB are as follows:
FNInit(A,"_sub1")
-- selects the compiled routine that
is to be called.
FNI(50)
-- passes the value 50 as a 16-bit INTEGER to
_sub1
.
FNL(J,1)
-- returns the 32-bit REAL value
J
to the calling routine.
FNS(S$,1)
-- returns the string
S$
to the calling routine.
LI>Line 240 selects the prototyper function called
FNCpr64
. Note that this function is type REAL (64-bit
floating-point compiled routine return value), which is the same type as
the compiled routine called _sub2
. A list of
execute CSUBs and functions can be found in the table entitled "Mapping
Between BASIC Return Value Type and Prototyper execute CSUBs or
Functions." The function parameters for this function are as follows:
FNInit(A,"_sub2")
-- selects the compiled routine to
be called.
FND(45)
-- passes the value 45 as a 64-bit floating point
REAL number to _sub2
.
LI>Line 270 uses the unload command called
Cprunload
to remove the object files from memory.
To exit the BASIC environment, execute the following statement:
QUIT [Return]
The BASIC default window should disappear. The window that the
rmb
command was executed in will appear with your HP-UX
shell prompt.
This step shows an example C program called example.c
that contains two CSUB routines. There are two important things to remember
during this step:
Here is a listing of the program example.c
, which defines
two simple C routines:
void sub1(i, j, str)
short i;
int *j;
char *str;
{
char *s="sub1";
*j=i;
strncpy(str, s, strlen(s)+1);
}
double sub2(r)
double r;
{
return r*2;
}
The first routine called sub1
simply assigns the value
of its first numeric parameter to its second numeric parameter, a variable
parameter, and copies characters into its string parameter. The second routine
called sub2
is a function which doubles the value of
its input parameter and returns the result. You should note that, unlike
CSUBs, these routines can have both value and variable formal parameters.
After compiling and testing the routines in C, you are now ready to generate a relocatable object file so that you can call the two routines from BASIC, using the prototyper.
The process of generating a relocatable object file as input to the prototyper
is the same as that used for creating a CSUB object file. In other words,
you use the HP-UX ld
command to link your object files
with the required libraries. The syntax for this command is as follows:
ld -rd -a archive example.o -u _printf -lrmb -o example
The above command generates the fully linked relocatable object file
example
, which will be used as input to the prototyper.
Before running the BASIC program called test_csubs
, enter
the BASIC environment by typing:
rmb [Return]
Now load the program called test_csubs
(found in
/usr/lib/rmb/demo) by executing the following command:
GET "test_csubs" [Return]
Note that you may have to use the statement MASS STORAGE IS to move to the
directory that contains test_csubs
.
To run the program test_csubs
, type:
RUN [Return]
or press [RUN] ([f3]).
After verifying the execution of compiled routines using the CSUB prototyper, you have two options to finalize the implementation of your program:
It makes sense to invoke the compiled routines using the prototyper if:
By converting the prototyper calls into direct CSUB calls, you can optimize the access time required to load the compiled routines with a CSUB interface. This option is also useful if you want to STORE a BASIC program with all of its CSUBs to make it self contained. The task of providing a CSUB interface is most easily done by defining a procedural interface for each of the selected compiled routines, as shown below for the current example:
void csub1(i, j, dim, str)
short *i;
double *j;
dimentryptr dim;
bstring_parm str;
{
int jj;
jj=(*j);
sub1(*i, &jj, str->c);
str->len=strlen(str->c)+1;
*j=jj;
}
double csub2(result, r)
double *result;
double *r;
{
*result=(*r)*2;
}
The main concern during this conversion process is to provide a CSUB interface with BASIC parameters that are compatible with those of the formal parameters of the compiled routines. This requirement may necessitate some parameter processing, as shown above, before and after the call to the compiled routines.
This section explains the parameter passing conventions of the prototyper and lists its parameter type mappings.
In order to correctly use the CSUB prototyper, it is necessary that you know its parameter passing conventions and the mappings that it allows between the types of BASIC parameters used to call compiled routines and the types of formal parameters and return values defined by the compiled routines.
The prototyper execute CSUB and functions can accept up to 16 parameters which correspond to the formal parameters of a given compiled routine. The BASIC variables or constants which are specified as parameters to these execute CSUBs may necessitate some type conversions before being passed to a compiled routine. Thus, you need to follow these conventions:
FNI(J,1)
passes an integer by reference FNI(J,0)
passes an integer by value Again, this specification
allows the prototyper to perform any necessary type conversions on parameters
after the call to the compiled routine.
void sub1(i)
short i;
Requires that you use the following
execute CSUB: Cpr(FNInit(A,"_sub1"), FNI(I,1))
This C CSUB routine returns an integer and does not have a parameter:
short sub2()
Requires that you use the following
execute CSUB: I=FNCpr16(FNInit(A,"_sub2"))
This section presents the mapping between the actual and formal parameters of compiled routines and mapping between BASIC return types and prototyper execute CSUBs. The following tables present this information.
CSUB Formal\Parameter Type | Parameter\Function | Variable Parameter\Supported | BASIC Actual\Parameter Type | Languages\Supported |
---|---|---|---|---|
8-bit character | FNC | Yes | INTEGER | all |
16-bit integer | FNI | Yes | INTEGER | all |
32-bit integer | FNI | No | INTEGER | all |
32-bit integer | FNL | Yes | REAL | all |
32-bit floating-point | FNR | Yes | REAL | all |
64-bit floating-point | FND | Yes | REAL | all |
32-bit pointer | FNP | Yes | REAL | Pascal, C |
string | FNS | Yes | string | all |
aggregate | FNA | Yes | REAL | Pascal, C |
You should note that the language of the routine can affect these specifications, and that some parameter types have restrictions on the use of variable parameters.
Compiled Routine\Return Value Type | Execute CSUB\or Function | BASIC Return\Value Type |
---|---|---|
void | Cpr | |
8-bit character | FNCpr8 | INTEGER |
16-bit integer | FNCpr16 | INTEGER |
32-bit integer | FNCpr32l | REAL |
32-bit floating-point | FNCpr32r | REAL |
64-bit floating-point | FNCpr64 | REAL |
32-bit pointer | FNCpr32p | REAL |
In addition to the information in these tables, each formal parameter and return value of a compiled routine may have some considerations that need to be discussed. These considerations are as follows:
8-bit character | The value of the INTEGER actual parameter should be within the range
of a byte. The prototyper takes care of making the proper format conversions
before and after the call to the compiled routine.
|
32-bit integer | The INTEGER actual parameter may not be a variable parameter since INTEGER
variables are only 16 bits wide. In order to have a variable parameter, you
should use a REAL variable with a value in the range of a 32-bit integer.
The prototyper takes care of making the proper format conversions before
and after the call to the compiled routine.
|
32-bit floating-point | The value of the REAL actual parameter should be within the range of
a 32-bit floating-point return value. The prototyper takes care of making
the proper format conversions before and after the call to the compiled routine.
|
32-bit pointer | Because BASIC does not define 32-bit scalar variables, the prototyper
stores pointer values in REAL variables. You should not attempt to print
or use such variables in BASIC since their values are only meaningful when
passed to compiled routines. The prototyper takes care of making the proper
format conversions before and after the call to the compiled routine.
|
String | The maximum length of a string actual parameter should always be greater
than the current length of the string by one because the prototyper converts
the storage for the string to a format suitable for the language of the compiled
routine. This means that a string literal may not be used as a parameter
to the string parameter function. Furthermore, variable string parameters
should have a maximum length large enough to accommodate any new string value
set by the compiled routine.
|
Aggregate | An aggregate, a Pascal record or a C struct/union, is specified by passing the pointer to it and its storage size. The prototyper takes care of passing the correct information to the compiled routine. There are two ways you can create an aggregate in BASIC: use a dynamic memory allocation routine in on of the compiled languages, or get a pointer to a named BASIC COM whose definition mirrors that of the aggregate. |
Prototyper CSUBs signal a run-time error with a BASIC CSUB run-time error message. In order to retrieve the actual error number, you should call the error CSUB (Cprerr) provided with the CSUB prototyper library (CPR). This CSUB returns a positive error number for each error generated by one of the prototyper CSUBs. The error numbers for the prototyper CSUBs are listed in the following table.
CSUB or\Function | Error Number | Description |
---|---|---|
Cprload | 1 | Out of memory |
Cprload | 2 | Object file access error |
Cprload | 3 | Relocation error |
FNInit | 1 | Compiled routine not found in object file |
FNS | 1 | Maximum string length too small |