Introduction
This example shows off many features of Fortran / C interoperability, including:
- the
ISO_C_BINDING
since Fortran 2003, allowing C-Fortran working together smoothly. - Interoperate between C struct and Fortran Derived Type.
- Use of C "
void * p
" parameter. - Passing Fortran function as an Argument to a C function.
- Use of Fortran Interfaces and Modules.
- Abstract C function, Abstract Fortran function.
- Using C's Struct or Fortran's Derived Type as arguments.
Background
A brief description of the example is that there is a function called gsl_UVMIF()
. One of its parameters is another function called with interface
fnExt(double, void*)
.
In actual usage, fnExt()
is an abstract representation of Fortran functions which can be any of the following:
f1(x, dpar3)
f2(x, dpar5)
f3(x, dpar10)
... where x
is double precision, and dpar3
, dpar5
,
dpar10
and whatever are a list of Fortran double precision parameters defined as Derived types, analogous to C-struct.
From the highest level, there is a Fortran driver program or code that will call C's
gsl_UVMIF()
, via a Fortran interface fgsl_UVMIF()
.
The gsl_UVMIF()
will call Fortran functions of the form fnExt(x, {parameters})
.
Using the code
The code is given as follows. At the core of the usage is the following C code.
#include "head.h"
void gsl_UVMIF(double (*fnExt)(double, void*) , dPar3 *adPar3, double blah, ....) {
....
.. fnExt .. *adPar3 }
In the header file head.h, define the C-struct which must have an exact correspondence to Fortran Derived Type. Note there is a 3-parameter and 10-parameter structure.
The prototype of the function is also declared.
extern "C"{
typedef struct {
double a,b,c;
} dPar3;
typedef struct {
double a,b,c,d,e, f,g,h,i,j;
} dPar10;
__declspec(dllexport) void gsl_UVMIF(double (*fnExt)(double, void*) , dPar3 *adPar3, double blah);
}
The details of the function fnExt
are defined on the Fortran side. But before doing that, we also need to define the list of parameters as Fortran Derived Type
which must exactly match the C struct data type. Define the structure in a module of its own.
MODULE extStructures
USE, INTRINSIC :: ISO_C_BINDING
implicit none
TYPE, BIND(C) :: dPar3
REAL(C_DOUBLE) :: a,b,c
END TYPE dPar3
TYPE, BIND(C) :: dPar10
REAL(C_DOUBLE) :: a,b,c,d,e, f,g,h,i,j
END TYPE dPar3
END MODULE extStructures
Define the Fortran interface called fgsl_UVMIF()
which connects directly to
gsl_UVMIF()
. It uses the parameter structure dPar3 defined in extStructures
above.
MODULE mod_f90
USE extStructures ! where parameter structures are defined above.
implicit none
INTERFACE
SUBROUTINE fgsl_UVMIF(fnExt , param3, blah, ....) BIND(C, NAME='gsl_UVMIF')
USE, INTRINSIC :: ISO_C_BINDING ! New Fortran 2003 standard
USE extStructures
IMPLICIT NONE
REAL(C_DOUBLE) :: fnExt ! External Fortran function defined by user.
EXTERNAL fnExt
TYPE(dPar3) :: param3 ! parameter with 3 doubles struct.
.....
END SUBROUTINE fgsl_UVMIF
END INTERFACE
END MODULE mod_f90
Here is one example of the Fortran function with three parameters. This code is defined outside any Fortran MODULE or PROGRAM. So the list of parameters,
dpar
, is passed in as a Fortran structure.
REAL(C_DOUBLE) FUNCTION Fext_dUVMIF(X, dpar)
USE, INTRINSIC :: ISO_C_BINDING
use mod_f90, only : dPar3
REAL(C_DOUBLE), VALUE :: X
TYPE (dPar3) dpar
Fext_dUVMIF = dPar%a*X*X + dPar%b*X + dPar%c
END FUNCTION Fext_dUVMIF
The main Fortran driver code is below.
PROGRAM main
USE mod_f90, only : fgsl_UVMIF
USE, INTRINSIC :: ISO_C_BINDING
real(C_DOUBLE) :: Fext_dUVMIF
external Fext_dUVMIF
TYPE(dPar3) :: param3
param3%a = 1.0d0
param3%b = -4.0d0
param3%c = 5.0d0
CALL fgsl_UVMIF(Fext_dUVMIF, param3, blah, ...)
END PROGRAM main
The call stack is roughly main()
-> fgsl_UVMIF()
-> gsl_UVMIF()
-> *fnExt()
-> Fext_dUVMIF()
.
Points of Interest
Key lessons were passing in a Fortran function as an argument of another C function, which is called within another Fortran code. Also passing a list of parameters
in C Struct format using it's Fortran equivalent of Derived Type.