-
Notifications
You must be signed in to change notification settings - Fork 16
Fortran iso_c_binding
Work with C functions in Fortran using iso_c_binding module
Suppose we have a file test_iso_c_bind.c
containing three C functions:
#include <stdio.h>
void Basics (const double a, // in
const int n, // in
double *b) // out
{
// bala bala
}
void Array (const double c[], // in
double d[]) // out
{
// bala bala
}
double Function (double (*f) (double, void(*)), // a function pointer
void * g) // a pointer to some type
{
// bala bala
}
Now the question is how to call those 3 functions in a Fortran program. One of the most common solutions is to use Fortran iso_c_binding
module. Next, we'll demonstrate the way to use iso_c_binding
to achieve this.
- Basics
Recall we have a C
function Basics
:
void Basics (const double a, // in
const int n, // in
double *b) // out
{
// bala bala
}
In this section, we'll show how to call Basics
function in a Fortran program test
.
program test
use iso_c_binding ! import iso_c_binding module
implicit none
!> Create an interface to C function
interface
subroutine Basics_c(a, n, b) bind(c, name='Basics')
use iso_c_binding, only :: c_int, c_double
real(kind=c_double), value, intent(in) :: a
integer(kind=c_int), value, intent(in) :: n
real(kind=c_double) :: b
end subroutine Basics_c
end interface
!> Define C-type variables
real(kind=c_double) :: a_c, b_c
integer(kind=c_int) :: n_c
!> Define Fortran-type variable
real :: a_f, b_f
integer :: n_f
!> Assign values to a_f and n_f
a_f = 1.5
n_f = 1
!> Convert Fortran to C
a_c = real(a_f, kind=c_double)
n_c = integer(n_f, kind=c_int)
!> Call C function
call Basics(a_c, n_c, b_c)
!> Convert C output to Fortran type
b_f = real(b_c)
end program test
Remarks:
- An
interface
block is created to allowtest
program to accessBasics
function because we cannot import aC
program to aFortran
program usinguse
statement. - The
name
statement is used because subroutine nameBasics_c
is different from function nameBasics
. It can be omitted ifFortran
subroutine name is made to be the same as theC
function name. - The
only
statement specifies the only types required by declaringC
functionBasics
. This is economical way of loadingiso_c_binding
module. If we are lazy about this, we can simply douse iso_c_binding
withoutonly
statement. - The
value
attribute in the declaration ensures we are passing actual value of a variable, rather than its reference.C
program is by default "call-by-value" whereasFortran
program uses "call-by-reference" paradigm. -
intent(in)
matchesconst
keyword in the variable declaration. But it's not necessary (i.e. the code will still work if we do not useintent(in)
). -
intent(out)
is typically not used because it has conflicts withvalue
attribute. - Because
b
in theBasics
function is declared to be a pointer (i.e. reference) todouble
, we don't supplyvalue
attribute. - Use
kind
statement to cast aFortran
data type to its correspondingC
type or vice versa. A complete list of interoperable data types betweenC
andFortran
can be found here.
- Array as argument
Recall we have a C
function Array
:
void Array (const double c[], // in
double d[]) // out
{
// bala bala
}
In this section, we'll show how to call Array
function in a Fortran program test
.
program test
use iso_c_binding ! import iso_c_binding module
implicit none
!> Create an interface to C function
interface
subroutine Array_c(c, d) bind(c, name='Array')
use iso_c_binding
type(c_ptr), value, intent(in) :: c ! double []
type(c_ptr), value :: d ! double []
end subroutine Basics_c
end interface
!> Define C-type variables
real(kind=c_double), dimension(2), target :: c_c, d_c
type(c_ptr) :: c_p, d_p
!> Define Fortran-type variable
real, dimension(2) :: c_f, d_f
!> Assign value to c_f
c_f = (/1.5, 2.5/)
!> Convert Fortran to C
c_c = real(c_f, kind=c_double)
!> Define C-type pointers
c_p = c_loc(c_c)
d_p = c_loc(d_c)
!> Call C function
call Array(c_p, d_p)
!> Convert C output to Fortran type
d_f = real(d_c)
end program test
Remarks:
-
c_ptr
is a genericC
pointer type that can point to any data type. Here in the example abovec_ptr
isdouble *
. InC
, array essentially can be viewed as a pointer to its first element. -
c_loc
returns a pointer ofc_ptr
type to aC
interoperable variable, which has eithertarget
attribute orpointer
type. Likec_ptr
,c_loc
is a generic function that can take anyC
interoperable data type.
- Function as argument
Recall we have a C
function Function
:
double Function (double (*f) (double, void(*)), // a function pointer
void * g) // a pointer to some type
{
// bala bala
}
In this section, we'll show how to call Function
function in a Fortran program test
. Passing a function to a another function becomes inevitable if say, we want to integrate a Fortran function using some external C function library (e.g. GNU Scientific Library).
module test_fun
use iso_c_binding
!> Create a C interoperable type
type, bind(c) :: params_t
real(kind=c_double) :: p
end type
contains
!> Define a C interoperable function
function f(x, params) bind(c)
real(kind=c_double), value :: x
type(params_t) :: params ! note: it's a pointer to params_t
real(kind=c_double) :: f
f = x**(params%p)*log(x)
end function f
end module test_fun
program test
use iso_c_binding ! import iso_c_binding module
use test_fun
implicit none
!> Create an interface to C function
interface
function Function(f, params) bind(c)
use iso_c_binding
type(c_funptr), value :: f ! double (*f) (double, void*)
type(c_ptr), value :: params ! void*
end function Function
end interface
!> Define C-type variables
type(c_funptr) :: f_p
type(params_t), target :: params_c
type(c_ptr) :: params_p
real(kind=c_double) :: result_c
!> Define Fortran-type variable
real :: result_f
!> Convert Fortran to C type
params_c%p = real(-0.5, kind=c_double)
!> Define C-type pointers
f_p = c_funloc(f)
params_p = c_loc(params_c)
!> Call C function
result_c = Function(f_p, params_p)
!> Convert C output to Fortran type
result_f = real(result_c)
end program test
Remarks:
-
void *
is a generic way inC
to declare a pointer that can point to any data type. - Just like
c_ptr
,c_funptr
is a pointer to aC
interoperable function. - Just like
c_loc
,c_funloc
returns aC
pointer to aC
interoperable function.
-
References
- Interoperability with C (GNU): https://gcc.gnu.org/onlinedocs/gfortran/Interoperability-with-C.html