- compiles a Fortran dynamic library containing a subroutine that accepts a double and a function pointer and simply calls the pointed function with the double as an argument,
- creates a test C program that passes a double and simple squaring function to the Fortran library,
- compiles a Cython class containing a method that passes a double and a simple squaring function to the Fortran library,
- executes a Python script that imports the Cython class and executes various methods.
First, let's take a look at the Fortran library. Fortran 95's way of defining function pointers is a bit different from C's method. Here's the code:
! clawpack.f95
subroutine evolve(q, rp)
! define inputs
double precision, intent(inout) :: q
interface
subroutine rp(q)
double precision, intent(inout) :: q
end subroutine
end interface
! execute input function
call rp(q)
end subroutine
We compile this using the following commands (Mac OS X):
$ gfortran -g -c -fPIC -arch i386 -arch ppc clawpack.f95 -o clawpack.o
$ gcc -arch i386 -arch ppc -dynamiclib clawpack.o -o libclawpack.dylib
I'll discuss why I explicitly include the above architecture flags when we get to the Cython portion of this discussion. In order to test this functionality, I wrote a little C program. But first, for C to interface with the library, we need a header file. Taking a look at the library symbol table will show that gfortran prefixes each subroutine with an underscore "_". Therefore, the header should look a little something like this:
// clawpack.h
extern void evolve_(double *q,
void (*rp)(double *q));
Note that Fortran subroutines actually accepts pointers to their inputs rather than the contents at the addresses, themselves; at least this is so from the C perspective. Therefore, we pass in a pointer to the double precision variable. Additionally, the C equivalent of a Fortran subroutine is
a void function. This website provides some nice examples of C/Fortran equivalences and communication.
Now for the program itself:
// test.c
#include
#include
#include "clawpack.h"
// an example function that we will pass to evolve_
void c_square(double *q);
void c_square(double *q) {
*q = (*q)*(*q);
}
int main(int argc, char **argv) {
double *val;
val = (double *) malloc( sizeof(double) );
if (argc == 1)
*val = 0.0;
else
*val = atof( argv[1] ); // obtain val as command line arg.
// square once
c_square(val);
printf("val ** 2 = %f\n", *val);
// square again from Fortran library
evolve_(val, c_square);
printf("val ** 4 = %f\n", *val);
free(val);
return 0;
}
Compile against the library with the header and you'll get something like this:
$ ./test_prog 2.0
val ** 2 = 4.000000
val ** 4 = 16.000000
$ ./test_prog 5.0
val ** 2 = 25.000000
val ** 4 = 625.000000
$
This means that function pointer passing from C to Fortran works! In particular, the value of val is retained throughout the process - important for when we begin passing around important information like adaptive meshes and boundary value data.
There's a lot that goes on in the Cython module; both in it's implementation and compilation. Therefore, I'll reserve an entire post for all of that information. Expect to see it tomorrow!
