No BASIC routine can be executed unless the main program is in BASIC, because a BASIC routine requires the environment to be initialized in a way that is unique to BASIC. No other language will perform this special initialization.
However, your program can start up in BASIC, call a C function that does most of the work of the program, and then call BASIC subprograms and function procedures as needed. Figure 11.3 illustrates how to do this.
Follow these rules when you call BASIC from C:
1.Start up in a BASIC main module. You will need to use the DECLARE statement to provide an interface to the C module.
2.In the C module, write a prototype for the BASIC routine and include type information for parameters. Use either the __fortran or __pascal keyword to modify the routine itself.
3.Make sure that all data are passed as near pointers. BASIC can pass data in a variety of ways but is unable to receive data in any form other than near reference. With near pointers, the program assumes that the data are in the default data segment. If you want to pass data that are not in the default data segment, copy the data to a variable in the default data segment.
4.Compile the C module in medium or large model to ensure far (intersegment) calls.
The example below demonstrates a BASIC program that calls a C function. The C function then calls a BASIC function that returns twice the number passed to it and a BASIC subprogram that prints two numbers.
' BASIC source
'
' The main program is in BASIC because of BASIC's startup
' requirements. The BASIC main program calls the C function
' Cprog.
'
' Cprog calls the BASIC subroutine Dbl.
'
DEFINT A-Z
DECLARE SUB Cprog CDECL()
CALL Cprog
END
'
FUNCTION Dbl(N) STATIC
Dbl = N*2
END FUNCTION
'
SUB Printnum(A,B) STATIC
PRINT “The first number is ”;A
PRINT “The second number is ”;B
END SUB
/* C source; compile in medium or large model */
int __fortran dbl( int __near * N );
void __fortran printnum( int __near * A, int __near * B );
void cprog()
{
int a = 5;
int b = 6;
printf( “%d times 2 is %d\n”, a, dbl( &a ) );
printnum( &a, &b );
}
In the previous example, note that the addresses of a and b are passed, since BASIC expects to receive addresses for parameters. This is important because C passes parameters by value unless you use the address-of (&) operator to obtain the address, or are passing an array. Also note that the function prototype for printnum declares the parameters as near pointers. The prototype causes the variables to be passed by near reference. If a or b is declared as __far, the C compiler issues a warning that you are converting a far pointer to a near pointer and that a segment was lost in the conversion.
Calling and naming conventions are resolved by the CDECL keyword in the BASIC declaration of Cprog, and by the __fortran keyword in the C declaration of dbl and printnum.
Summary: BASIC can invoke one of your functions as part of the termination procedure.
Versions of QuickBASIC later than 4.0 provide a “user entry point,” B_OnExit, which can be called directly from C. The B_OnExit function enables you to make sure you have performed an orderly termination. The following code shows how to use B_OnExit.
#include <malloc.h> /* For declaration of _fmalloc */
#include <stdlib.h> /* For declaration of onexit_t */
/* The prototype for B_OnExit declares it as a function
* returning type onexit_t that takes one parameter. The
* parameter is a far pointer to a function that returns
* no value.
*/
extern onexit_t __pascal __far B_OnExit( onexit_t );
void TermProc( void );
int * p_IntArray;
void InitProc( void )
{
/* Allocate far space for 20-integer array */
p_IntArray = (int *)_fmalloc( 20 * sizeof( int ) );
/* Log termination routine (TermProc) with BASIC. */
B_OnExit( TermProc );
}
void TermProc( void )
{
free( p_IntArray ); /* Release far space allocated */
} /* previously by InitProc. */