Compaq C
Compaq C User's Guide for OpenVMS Systems
3.3.2 Calling VAX MACRO
You can call a VAX MACRO routine from Compaq C or vice versa.
However, like all interlanguage calls, it is necessary to make sure
that the actual arguments correspond to the expected formal parameter
types. Also, it is necessary to remember that C strings are
null-terminated and to take special action in either the MACRO routine
or the C routine to allow for this.
Example 3-8 shows a MACRO routine that calls a C routine with three
arguments, passing one by value, one by reference, and one by
descriptor. It is followed by the source for the called C routine.
Example 3-8 VAX MACRO Program Calling a
Compaq C Function |
;-----------------------------------------------------------------------
; Beginning of MACRO program
;-----------------------------------------------------------------------
.extrn dbroutine ; The C routine
;-----------------------------------------------------------------------
; Local Data
;-----------------------------------------------------------------------
.psect data rd,nowrt,noexe
ft$$t_part_num: .ascii /WidgitGadget/
ft$$t_query_mode: .ascii /I/
ft$$s_query_mode = <. - ft$$t_query_mode>
ft$$l_protocol_buff: .blkl 1
ft$$kd_part_num_dsc:
.word 12
.word 0
.address ft$$t_part_num
;-----------------------------------------------------------------------
; Entry Point
;-----------------------------------------------------------------------
.psect ft_code rd,nowrt,exe
.entry dbtest ^m<r2,r3,r4,r5,r6,r7,r8>
;+
; call C routine for data base lookup
;-
movl #1,r3
pushal ft$$kd_part_num_dsc ; Descriptor for part number
pushal ft$$t_query_mode ; Mode to call
pushl #1 ; Status
calls #3, dbroutine ; Check the data base
99$:
ret
.end dbtest
;-----------------------------------------------------------------------
; End of MACRO program
;-----------------------------------------------------------------------
/*
* Beginning of Compaq C code for dbroutine:
*/
#include <stdio.h>
#include <descrip.h>
/* Structure pn_desc is the format of the descriptor
passed by the macro routine. */
extern struct
mydescript {
short pn_len;
short pn_zero;
char *pn_addr;
};
int dbroutine (int status, /* Passed by value */
char *action, /* Passed by reference */
struct mydescript *name_dsc) /* Passed by descriptor */
{
char *part_name;
/* Allocate space to put the null-padded name string. */
part_name = malloc(name_dsc->pn_len + 1);
memcpy( part_name,name_dsc -> pn_addr ,name_dsc -> pn_len);
/* Remember that C array bounds start at 0 */
part_name[name_dsc -> pn_len] = '\0';
printf (" Status is %d\n", status);
printf (" Length is %d\n",name_dsc -> pn_len);
printf (" Part_name is %s\n",part_name);
printf (" Request is %c\n",*action);
status = 1;
return(status);
} /* End of Compaq C code for dbroutine */
|
Example 3-8 produces the following output:
Status is 1
Length is 12
Part_name is WidgitGadget
Request is I
|
Example 3-9 shows a Compaq C program that calls a VAX MACRO
program.
Example 3-9 Compaq C Program Calling a
VAX MACRO Program |
/* Beginning of Compaq C function */
#include <stdio.h>
#include <descrip.h>
int zapit( int status, int *action, struct dsc$descriptor_s *descript);
main(void)
{
int status=255, argh = 99;
int *action = &argh;
$DESCRIPTOR(name_dsc,"SuperEconomySize");
printf(" Before calling ZAPIT: \n");
printf(" Status was %d \n",status);
printf(" Action contained %d and *action contained %d \n" ,action, *action);
printf(" And the thing described by the descriptor was %s\n",
name_dsc.dsc$a_pointer);
if (zapit(status,action,&name_dsc) && 1)
{
printf(" Ack, the world has been zapped! \n");
printf(" Status is %d \n",status);
printf(" Action contains %d and *action contains %d \n" ,action, *action);
printf(" And the address of the thing described by the descriptor is %d\n",
name_dsc.dsc$a_pointer);
}
} /* End of Compaq C function
;-----------------------------------------------------------------------
; Beginning of VAX MACRO source code for zapit
;-----------------------------------------------------------------------
; Entry Point
;-----------------------------------------------------------------------
.psect ft_code rd,nowrt,exe
.entry zapit ^m<r2,r3,r4,r5,r6,r7,r8>
;+
; Maliciously change parameters passed by the C routine.
;
; The first parameter is passed by value, the second by
; reference, and the third by descriptor.
;-
movl 4(ap), @8(ap) ;Change the by-reference parameter
;to the first parameter's value.
movl 12(ap), r2
movl #0,4(r2) ;Zap address of string in descriptor.
; Return -1 to signal successful destruction.
movl #-1,r0
ret
.end
;-----------------------------------------------------------------------
; End of VAX MACRO source code for zapit
;-----------------------------------------------------------------------
|
Example 3-9 produces the following output:
Before calling ZAPIT:
Status was 255
Action contained 2146269556 and *action contained 99
And the thing described by the descriptor was SuperEconomySize
Ack, the world has been zapped!
Status is 255
Action contains 2146269556 and *action contains 255
And the address of the thing described by the descriptor is 0
|
3.3.3 Calling Compaq BASIC
Calling routines written in Compaq BASIC from Compaq C programs
or vice versa is straightforward. By default, Compaq BASIC passes
arguments by reference, except for arrays and strings, which are passed
by descriptor. In some cases, these defaults may be overridden by
explicitly specifying the desired parameter-passing mechanisms in the
Compaq BASIC program. However, if an argument is a constant or an
expression, the actual argument passed refers to a local copy of the
specified argument's value.
Strings in Compaq BASIC are not terminated by a null character, which
is done by Compaq C. As a result, passing strings between
Compaq BASIC and Compaq C routines requires you to do additional
work. You may choose to add an explicit null character to a
Compaq BASIC string before passing it to a Compaq C routine, or
you may prefer to code the Compaq C routine to obtain the string's
length from its descriptor.
Example 3-10 shows a Compaq C program that calls a Compaq BASIC
function with a variety of argument data types.
Example 3-10 Compaq C Function Calling a
Compaq BASIC Function |
/*
* Beginning of Compaq C function:
*/
#include <stdio.h>
#include <descrip.h>
extern int basfunc ();
main(void)
{
int i = 508;
float f = 649.0;
double d = 91.50;
struct
{
short s;
float f;
} s = { -2, -3.14 };
$DESCRIPTOR (string1, "A C string");
printf ("BASIC returned %d\n",
basfunc (&i, &f, &d, &s, &string1, "bye"));
} /* End of Compaq C function */
REM Beginning of the BASIC program
FUNCTION INTEGER basfunc (INTEGER i, REAL f, DOUBLE d, x s, &
STRING string1, &
STRING string2 = 3 BY REF)
RECORD x
WORD s
REAL f
END RECORD x
PRINT 'i = '; i
PRINT 'f = '; f
PRINT 'd = '; d
PRINT 's::s = '; s::s
PRINT 's::f = '; s::f
PRINT 'string1 = '; string1
PRINT 'string2 = '; string2
END FUNCTION -15
REM End of the BASIC program
|
Example 3-10 produces the following output:
i = 508
f = 649
d = 91.5
s::s = -2
s::f = -3.14
string1 = A C string
string2 = bye
BASIC returned -15
|
Example 3-11 shows a Compaq BASIC program that calls a Compaq C
function.
Example 3-11 Compaq BASIC Program Calling a
Compaq C Function |
REM Beginning of the BASIC program:
PROGRAM example
EXTERNAL STRING FUNCTION cfunc (INTEGER BY VALUE, &
INTEGER BY VALUE, &
STRING BY DESC)
s$ = cfunc (5, 3, "abcdefghi")
PRINT "substring is "; s$
END PROGRAM
REM End of the BASIC program
/*
* Beginning of Compaq C function:
*/
#include <descrip.h>
/*
* This routine simulates a BASIC function whose return
* value is a STRING. It returns the substring that is `length'
* characters long, starting from the offset `offset' (0-based)
* in the input string described by the descriptor pointed to
* by `in_str'.
*/
void cfunc (struct dsc$descriptor_s *out_str,
int offset,
int length,
struct dsc$descriptor_s *in_str)
{
/* Declare a string descriptor for the substring. */
struct dsc$descriptor temp;
/* Check that the desired substring is wholly
within the input string. */
if (offset + length > in_str -> dsc$w_length)
return;
/* Fill in the substring descriptor. */
temp.dsc$w_length = length;
temp.dsc$a_pointer = in_str -> dsc$a_pointer + offset;
temp.dsc$b_dtype = DSC$K_DTYPE_T;
temp.dsc$b_class = DSC$K_CLASS_S;
/* Copy the substring to the return string. */
str$copy_dx (out_str, & temp);
} /* End of Compaq C function */
|
Example 3-11 produces the following output:
3.3.4 Calling Compaq Pascal
Like Compaq FORTRAN and Compaq BASIC, there are certain considerations
that you must take into account when calling Compaq Pascal from
Compaq C and vice versa. When calling Compaq Pascal from
Compaq C, Compaq Pascal expects all parameters to be passed by
reference. In Compaq Pascal, there are two different types of
semantics: value and variable. The value semantics in Compaq Pascal are
different from passing by value in Compaq C. Because they are
different, you must specify the address of the C parameter.
Compaq Pascal also expects all strings to be passed by descriptor. If
you use the CLASS_S descriptor, the string is passed by using
Compaq Pascal semantics. If the content of the string is changed, it is
not reflected back to the caller.
Example 3-12 is an example of how to call a Compaq Pascal routine from
Compaq C.
Example 3-12 Compaq C Function Calling a
Compaq Pascal Routine |
/*
* Beginning of Compaq C function:
*/
#include <descrip.h>
/* This program demonstrates how to call a Pascal routine
from a C function. */
/* A Pascal routine called by a C function. */
extern void Pascal_Routine ();
main()
{
struct dsc$descriptor_s to_Pascal_by_desc;
char *Message = "The_Max_Num";
int to_Pascal_by_value = 100,
to_Pascal_by_ref = 50;
/* Construct the descriptor. */
to_Pascal_by_desc.dsc$a_pointer = Message;
to_Pascal_by_desc.dsc$w_length = strlen (Message);
to_Pascal_by_desc.dsc$b_class = DSC$K_CLASS_S;
to_Pascal_by_desc.dsc$b_dtype = DSC$K_DTYPE_T;
/* Pascal expects a calling routine to pass parameters by reference. */
Pascal_Routine(&to_Pascal_by_value, &to_Pascal_by_ref, &to_Pascal_by_desc);
printf ("\nWhen returned from Pascal:\nto_Pascal_by_value is still \
%d\nBut to_Pascal_by_ref is %d\nand Message is still %s\n",
to_Pascal_by_value, to_Pascal_by_ref,
to_Pascal_by_desc.dsc$a_pointer);
} /* End of Compaq C function */
{
Beginning of Pascal routine
}
MODULE C_PASCAL(OUTPUT);
{ This Pascal routine calls the Pascal MAX function
to determine the maximum value between
'from_c_by_value` and 'from_c_by_ref`, and then
assigns the result back to 'from_c_by_ref`.
It also tries to demonstrate the results of passing
a by-descriptor mechanism.
It is called from a C main function.
}
[GLOBAL]PROCEDURE Pascal_Routine
( from_c_by_value :INTEGER;
VAR from_c_by_ref :INTEGER;
from_c_by_desc :[ CLASS_S ] PACKED ARRAY [l1..u1:INTEGER] OF CHAR
);
VAR
today_is : PACKED ARRAY [1..11] OF CHAR;
BEGIN
{ Display the contents of formal parameters. }
WRITELN;
WRITELN ('Parameters passed from C function: ');
WRITELN ('from_c_by_value: ', from_c_by_value:4);
WRITELN ('from_c_by_ref: ', from_c_by_ref:4);
WRITELN ('from_c_by_desc: ', from_c_by_desc);
{ Assign the maximum value into 'from_c_by_ref` }
from_c_by_ref := MAX (from_c_by_value, from_c_by_ref);
{ Change the content of 'from_Pascal_by_value` --
to show that the value did not get
reflected back to the caller.
}
from_c_by_value := 20;
{ Put the results of DATE into 'from_c_by_desc`
to show that the CLASS_S is only valid with
comformant strings passed by value.
}
DATE (today_is);
from_c_by_desc := today_is;
WRITELN ('***********************');
WRITELN ('from_c_by_desc is changed to today''s date: "',
from_c_by_desc, '"');
WRITELN ('BUT, this will not reflect back to the caller.');
END;
END.
{
End of Pascal routine
}
|
Example 3-12 produces the following output:
from_c_by_value: 100
from_c_by_ref: 50
from_c_by_desc: The_Max_Num
***********************
from_c_by_desc is changed to today's date "26-MAY-1992"
BUT, this will not reflect back to the caller.
When returned from Pascal:
to_Pascal_by_value is still 100
to_Pascal_by_ref is 100
and Message is still The_Max_Num
|
There are also some considerations when calling Compaq C from
Compaq Pascal. For example, you can use mechanism specifiers such as
%IMMED, %REF, and %STDESCR in Compaq Pascal. When you use the %IMMED
mechanism specifier, the compiler passes a copy of a value rather than
an address. When you use the %REF mechanism specifier, the address of
the actual parameter is passed to the called routine, which is then
allowed to change the value of the corresponding actual parameter. When
you use the %STDESCR mechanism specifier, the compiler generates a
fixed-length descriptor of a character-string variable and passes its
address to the called routine. For more information on these mechanism
specifiers and others, see the Compaq Pascal documentation.
Another consideration is that Compaq Pascal does not null-pad strings.
Therefore, you must add a null character to make the string a C string.
Also, when passing a string from Compaq Pascal to Compaq C, you
can declare a structure declaration in Compaq C that corresponds
to the Compaq Pascal VARYING TYPE declaration.
Example 3-13 shows an example of how to call Compaq C from
Compaq Pascal.
Example 3-13 Compaq Pascal Program Calling a
Compaq C Function |
{
Beginning of Pascal function:
}
PROGRAM PASCAL_C (OUTPUT);
CONST
STRING_LENGTH = 80;
TYPE
STRING = VARYING [STRING_LENGTH] OF CHAR;
VAR
by_value : INTEGER;
by_ref : STRING;
by_desc: PACKED ARRAY [1..10] OF CHAR;
[EXTERNAL]
PROCEDURE VAXC$CRTL_INIT; EXTERN;
[EXTERNAL]
PROCEDURE c_function
( %immed by_value : INTEGER;
%ref by_ref : STRING ;
%stdescr by_desc: PACKED ARRAY [l1..u1:INTEGER] OF CHAR
); EXTERN;
BEGIN
{ Establish the appropriate Compaq C RTL environment for
calling the Compaq C RTL from Pascal.
}
VAXC$CRTL_INIT;
by_value := 1;
{ NOTE
Pascal does not null pad a string.
Therefore, the LENGTH built-in function counts
the null pad character while the Compaq C library function strlen
does not include the terminating null character.
}
by_ref := 'TO_C_BY_REF'(0)'';
by_desc := 'TERM'(0)'';
{ Call a C function by passing parameters
using foreign semantics.
}
c_function (by_value, by_ref, by_desc);
WRITELN;
WRITELN;
WRITELN ('*************************');
WRITELN ('After calling C_FUNCTION: ');
WRITELN;
WRITELN ('by_value is still ',by_value:3);
WRITELN ('however, by_ref contains ',by_ref,
' (aka Your Terminal Type)');
WRITELN ('and, by_desc still contains ',by_desc);
END.
{
End of Pascal program
}
/*
* Beginning of Compaq C function:
*
*
* A C function called from the Pascal routine.
* The parameters are passed to a C function
* by value, by reference, and by descriptor,
* respectively.
*/
#include <descrip.h>
/* A Pascal style of VARYING data type. */
struct Pascal_VARYING
{
unsigned short length;
char string[80];
};
/* This C function calls the Compaq C RTL function getenv() and puts
* your terminal type in 'from_Pascal_by_ref`.
* It is called from a Pascal program.
*/
void c_function (unsigned char from_Pascal_by_value,
struct Pascal_VARYING *from_Pascal_by_ref,
struct dsc$descriptor_s *from_Pascal_by_desc
)
{
char *term;
/* Display the contents of formal parameters. */
printf ("\nParameters passed from Pascal:\n");
printf ("from_Pascal_by_value: %d\nfrom_Pascal_by_ref: %s\n\
from_Pascal_by_desc: %s\n", from_Pascal_by_value,
from_Pascal_by_ref -> string,
from_Pascal_by_desc -> dsc$a_pointer);
if ((term = getenv(from_Pascal_by_desc -> dsc$a_pointer)) != 0)
{
/* Fill 'from_Pascal_by_ref` with new value. */
strcpy (from_Pascal_by_ref -> string, term);
from_Pascal_by_ref -> length = strlen (term);
/* Change the contents of 'from_Pascal_by_value` --
* to demonstrate that the value did not get
* reflected back to the calling routine.
*/
from_Pascal_by_value = from_Pascal_by_desc -> dsc$w_length
+ from_Pascal_by_ref -> length;
}
else
printf ("\ngetenv\(\"TERM\"\) is undefined.");
} /* End of Compaq C function */
|
Example 3-13 produces the following output:
Parameters passed from Pascal:
from_Pascal_by_value: 1
from_Pascal_by_ref: TO_C_BY_REF
from_Pascal_by_desc: TERM
*************************
After calling C_FUNCTION:
by_value is still 1
however, by_ref contains vt200-80 (aka Your Terminal Type)
and, by_desc still contains TERM
|
3.4 Sharing Global Data
The following sections describe the methods involved in sharing
Compaq C program sections with data declared in other
OpenVMS languages.
3.4.1 Sharing Program Sections with FORTRAN Common Blocks
In a FORTRAN program, separately compiled procedures can share data in
declared common blocks, which specify the names of one or more
variables to be placed in them. Each named common block represents a
separate program section. Each procedure that declares the common block
with the same name can access the same variable.
Example 3-14 shows a Compaq C
extern
variable that corresponds to a FORTRAN common block with the same name.
Example 3-14 Sharing Data with a FORTRAN
Program in Named Program Sections |
C FORTRAN program PRSTRING.FOR contains the following lines of code:
SUBROUTINE PRSTRING
CHARACTER*20 STRING
COMMON /XYZ/ STRING
TYPE 20, STRING
20 FORMAT (' ',A20)
RETURN
END
C End of FORTRAN program
/* Compaq C program STRING.C contains the following lines of *
* code: */
main(void)
{
#pragma extern_model common_block // Alpha only. On VAX systems, use
// #pragma extern_model common_block shr
extern char xyz[20];
strncpy(xyz,"This is a string ", sizeof xyz);
prstring();
}
|
In Example 3-14, the Compaq C
extern
variable
xyz
corresponds to the FORTRAN common block named XYZ. The FORTRAN
procedure displays the data in the block. When sharing program
sections, both programs should declare corresponding variables to be of
the same type.
Note the
#pragma extern_model common_block
preprocessor directive. This directive sets the model for external
variables to the
common_block
model, which is the one used by VAX C. The default external model for
Compaq C is the
relaxed_refdef
model. For more information on the
#pragma extern_model common_block
preprocessor directive, see Section 5.4.5.
To share data in more than one variable in a program section with a
FORTRAN program, the Compaq C variables must be declared within a
structure, as shown in Example 3-15.
Example 3-15 Sharing Data with a FORTRAN
Program in a Compaq C Structure |
C FORTRAN program FNUM.FOR contains the following lines of code:
SUBROUTINE FNUM
INTEGER*4 INUM,JNUM,KNUM
COMMON /NUMBERS/ INUM,JNUM,KNUM
TYPE 10, (INUM,JNUM,KNUM)
10 FORMAT (318)
RETURN
END
C End of FORTRAN program
/* Compaq C program NUMBERS.C contains the following lines of *
* code: */
struct xs
{
int first;
int second;
int third;
};
#pragma extern_model common_block
main()
{
extern struct xs numbers;
numbers.first = 1;
numbers.second = 2;
numbers.third = 3;
fnum();
}
|
In Example 3-15, the
int
variables declared in the Compaq C structure numbers correspond to
the FORTRAN INTEGER*4 variables in the COMMON of the same name.
Also, note the
#pragma extern_model common_block
preprocessor directive. This directive sets the model for external
variables to the
common_block
model, which is the one used by VAX C. The default external model for
Compaq C is the
relaxed_refdef
model. For more information on the
#pragma extern_model common_block
preprocessor directive, see Section 5.4.5.
|
|