The macros can be used in a C function by including the file f77.h. This file will naturally be stored in different places on different types of system, even if it is only the syntax of the file name that is different. It would be a pity if all of the implementation specific details were hidden away in these macros, only to have to have an implementation specific #include statement in each C source file. Fortunately there is a way around this problem that is described in compiling and linking.
Let us now consider an example of using the F77 macros to illustrate their use. The following example generates a banner which consists of some hyphens, followed by some stars and finally the same number of hyphens again. There are also some blanks between the beginning of the line and between the hyphens and stars. The work is done in the subroutine BANNER and the form of the output is governed by the three arguments FIRST, MIDDLE and GAP. For example, CALL BANNER( LINE, 5, 10, 3 ) would return with LINE set to the following character string.
----- ********** -----
PROGRAM F1 INTEGER FIRST, MIDDLE, GAP CHARACTER*(80) LINE FIRST = 5 MIDDLE = 10 GAP = 3 CALL BANNER( LINE, FIRST, MIDDLE, GAP ) PRINT *, LINE ENDC function:
#include "f77.h" F77_SUBROUTINE(banner)( CHARACTER(line), INTEGER(first), INTEGER(middle), INTEGER(gap) TRAIL(line) ) { GENPTR_CHARACTER(line) GENPTR_INTEGER(first) GENPTR_INTEGER(middle) GENPTR_INTEGER(gap) int i, j; /* Loop counters. */ char *cp; /* Pointer to a character. */ /* Make cp point to the beginning of the string line. */ cp = line; /* First blanks. */ for( i = 0, j = 0 ; (j < line_length) && (i < *gap) ; i++, j++ ) *cp++ = ' '; /* First hyphens. */ for( i = 0 ; (j < line_length) && (i < *first) ; i++, j++ ) *cp++ = '-'; /* More blanks. */ for( i = 0 ; (j < line_length) && (i < *gap) ; i++, j++ ) *cp++ = ' '; /* Middle stars. */ for( i = 0 ; (j < line_length) && (i < *middle) ; i++, j++ ) *cp++ = '*'; /* More blanks. */ for( i = 0 ; (j < line_length) && (i < *gap) ; i++, j++ ) *cp++ = ' '; /* Last hyphens. */ for( i = 0 ; (j < line_length) && (i < *first) ; i++, j++ ) *cp++ = '-'; }
The FORTRAN part of this example is completely standard; it is the C code that need further explanation. Firstly there is the declaration of the subroutine with the macro F77_SUBROUTINE. This handles the fact that some computer systems require a trailing underscore to be added to the name of the C function if it is to be called from FORTRAN. In the same statement there are the function's dummy arguments, declared using the macros CHARACTER, INTEGER and TRAIL. The INTEGER macro declares the appropriate dummy argument to handle an incoming argument passed from FORTRAN. This will usually be declared to be ``pointer to int''. The CHARACTER and TRAIL macros come in pairs. The CHARACTER macro declares the appropriate argument to handle the incoming character variable and TRAIL may declare an extra argument to handle those cases where an extra hidden argument is added to specify the length of a character argument. On some machines, TRAIL will be null and on account of this there should not be a comma before any TRAIL macros. When TRAIL is not null, then it will add the comma itself. If there are several TRAIL macros then there must not be a comma directly in front of any of them.
The next set of macros are the GENPTR_type macros, one for each argument of the FORTRAN subroutine (TRAIL arguments are not counted as separate arguments for this purpose). These handle the ways that subprogram arguments may be passed on different machines. They ensure that a pointer to the argument exists. On most systems, this is exactly what is passed from the FORTRAN program and so the macros for numeric arguments are null. If a particular system passed the value of an argument, rather than its address, then these macros would generate the appropriate pointers.
The CHARACTER, TRAIL and GENPTR_CHARACTER macros have to cope with the different ways that systems deal with passing character variables. Although the way that these macros are implemented can be a bit complex, what the programmer sees is essentially simple. For each character argument, the macros generate a pointer to a character variable and an integer holding the length of that character variable. The above example will create the variable line of type char * and variable line_length of type int. If these are available directly as function arguments, then the macro GENPTR_CHARACTER will be null, otherwise it will generate these two variables from the arguments. The best way of seeing what is going on is to compile a function with macro expansion turned on and list the output.
There is an important difference between this example and the one in the cookbook. In this case, an int variable containing the length of the character argument is generated automatically whereas in the example in the cookbook the length was passed explicitly. In fact, the int variable was also generated in the example in the cookbook, but it was not used. It is more portable to explicitly pass the length of CHARACTER variables and to ignore the automatically generated length as this will cope with the situation where the length cannot be generated automatically. No such machines are known to the author at present, but Murphy's Law would indicate that the next machine that we desperately need to use will have this problem.
Although the use of these macros does seems a bit strange at first, once any pointers have been generated, the rest of the code is standard C.
Something that has not yet been considered is whether to write the code in upper or lower case. All of the examples in this document have the FORTRAN code in upper case and the C code in lower case, thereby following common practice. Normally it makes no difference whether code is written in upper case or lower case. Where it does matter is in declaring external symbols. External symbols are names of routines and names of common blocks (FORTRAN) or variables declared extern (C). The linker must be able to recognise that the external symbols in the FORTRAN routines are the same external symbols in the C functions. On a VMS system, the VAX C compiler will fold all external symbols to upper case by default, although there is a compiler option to fold them all to lower case or leave them as written in the source code. The VAX FORTRAN compiler will generate all external symbols in upper case. On Unix systems, the FORTRAN compiler will typically fold external names to lower case (and add a trailing underscore), whereas the C compiler will leave the case unchanged. Consequently, all external symbols in C functions that might be referenced from FORTRAN should be coded in lower case.
CNF and F77 Mixed Language Programming -- FORTRAN and C