Example: Calling a Fortran Routine Using a C Interface Routine


Calling Fortran is similar to calling C, with the significant difference that Fortran code expects all arguments to be passed by reference and not by value (the C default). This means that the address of the argument is passed rather than the argument itself. This issue is discussed in By-Value and By-Reference Arguments.

A C interface routine can easily extract the addresses of the arguments from the argv array and pass them to the actual routine which will compute the sum. The arguments f, n, and s are pointers that are being passed by value. Fortran expects all arguments to be passed by reference; it expects all arguments to be addresses. If C passes a pointer (an address) by value, Fortran will interpret it correctly as the address of an argument. The following code segments illustrate this. The example_c2f.c file contains the C interface routine, which would be compiled as illustrated above. The example.f file contains the Fortran routine that actually sums the array.

In these examples, the object name of the Fortran subroutine will be sum_array1_ to match the output of the Fortran compiler. The following are the contents of example_c2f.c and example.f:

#include <stdio.h>
 
void sum_array(int argc, void *argv[])
{
 extern void sum_array1_();/* Fortran routine */
 int *n;
	float *s, *f;
 
	f = (float *) argv[0];	/* Array pntr */
	n = (int *) argv[1];	/* Get # of elements */
	s = (float *) argv[2];	/* Pass back result a parameter */
 
	sum_array1_(f, n, s);	/* Compute sum */
}

 

f77

 

c This subroutine is called by SUM_ARRAY and has no IDL-specific code.
c
SUBROUTINE sumarray1(array, n, sum)
INTEGER*4 n
REAL*4 array(n), sum
 
 sum=0.0
 DO i=1,n
 sum = sum + array(i)
 PRINT *, sum, array(i)
 ENDDO
 
 RETURN
 END

 

This example is compiled and linked in a manner similar to that used in the C example above. For more information on compiling and linking on your platform, see the README file contained in the external/call_external/Fortran subdirectory of the IDL distribution. This directory also contains a makefile, which builds this example on UNIX platforms. To call the example program from within IDL:

;Make an array. X = FINDGEN(10)
;A floating result
SUM = 0.0
S = CALL_EXTERNAL('example.so', $
   'sum_array', X, N_ELEMENTS(X), sum)

In this example, example.so is the name of the sharable image file, sum_array is the name of the entry point, and X and N_ELEMENTS(X) are passed to the called routine as parameters. The returned value is contained in the variable sum.

Hidden Arguments


When passing C null-terminated character strings into a Fortran routine, the C function should also pass in the string length. This extra parameter is added to the end of the Fortran routine call in the C function, but does not explicitly appear in the Fortran routine.

For example, in C:

char * str1= 'IDL';
char * str2= 'ITT';
 
int len1=3;
int len2=3;
double data, info;
/* Call a Fortran sub-routine named example1 */
example1_(str1, data, str2, info, len1, len2)

In Fortran:

SUBROUTINE EXAMPLE1(STR1, DATA, STR2, INFO) CHARACTER*(*)STR1, STR2
DOUBLE PRECISIONDATA, INFO

Example: Calling a Fortran Routine Using a Fortran Interface Routine


Calling Fortran is similar to calling C, with the significant difference that Fortran expects all arguments to be passed by reference. This means that the address of the argument is passed rather than the argument itself.

A Fortran interface routine can be written to extract the addresses of the arguments from the argv array and pass them to the actual routine which will compute the sum. Passing the contents of each argv element by value has the same effect as converting the parameter to a normal Fortran parameter.

This method uses the OpenVMS Extensions to Fortran, %LOC and %VAL.

Some Fortran compilers may not support these extensions. If your compiler does not, use the method discussed in the previous section for calling Fortran with a C interface routine.

The contents of the file example1.f are shown in the following figure. This example is compiled, linked, and called in a manner similar to that used in the C example above. For more information on compiling and linking on your platform, see the README file contained in the external/fortran subdirectory of the IDL distribution. This directory also contains a makefile, which builds this example on UNIX platforms.

 

f77
 
 SUBROUTINE SUM_ARRAY(argc, argv)	!Called by IDL
 INTEGER*8 argc, argv(*)	!Argc and Argv are integers
 
 j = LOC(argc)	!Obtains the number of arguments (argc)
	!Because argc is passed by VALUE.
 
 c Call subroutine SUM_ARRAY1, converting the IDL parameters
 c to standard Fortran, passed by reference arguments:
 
 CALL SUM_ARRAY1(%VAL(argv(1)), %VAL(argv(2)), %VAL(argv(3)))
 RETURN
 END
 
 c This subroutine is called by SUM_ARRAY and has no
 c IDL specific code.
 c
 SUBROUTINE SUM_ARRAY1(array, n, sum)
 INTEGER*4 n
 REAL*8 array(n), sum
 
 sum=0.0
 DO i=1,n
 sum = sum + array(i)
 ENDDO
 RETURN
 END

 

To call the example program from within IDL:

X = FINDGEN(10) ; Make an array. sum = 0.0
S = CALL_EXTERNAL('example1.so', $
   'sum_array_', X, N_ELEMENTS(X), sum)

In this example, example1.so is the name of the sharable image file, sum_array_ is the name of the entry point, and X and N_ELEMENTS(X) are passed to the called routine as parameters. The returned value is contained in the variable sum.

Note: The entry point name generated by the Fortran compiler may be different than that produced by the C compiler. One of the best ways to find out what name was generated is to use the UNIX nm utility on the object file. See your system’s man page for nm for details.