-
Notifications
You must be signed in to change notification settings - Fork 17
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 testRemarks:
- An
interfaceblock is created to allowtestprogram to accessBasicsfunction because we cannot import aCprogram to aFortranprogram usingusestatement. - The
namestatement is used because subroutine nameBasics_cis different from function nameBasics. It can be omitted ifFortransubroutine name is made to be the same as theCfunction name. - The
onlystatement specifies the only types required by declaringCfunctionBasics. This is economical way of loadingiso_c_bindingmodule. If we are lazy about this, we can simply douse iso_c_bindingwithoutonlystatement. - The
valueattribute in the declaration ensures we are passing actual value of a variable, rather than its reference.Cprogram is by default "call-by-value" whereasFortranprogram uses "call-by-reference" paradigm. -
intent(in)matchesconstkeyword 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 withvalueattribute. - Because
bin theBasicsfunction is declared to be a pointer (i.e. reference) todouble, we don't supplyvalueattribute. - Use
kindstatement to cast aFortrandata type to its correspondingCtype or vice versa. A complete list of interoperable data types betweenCandFortrancan 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 testRemarks:
-
c_ptris a genericCpointer type that can point to any data type. Here in the example abovec_ptrisdouble *. InC, array essentially can be viewed as a pointer to its first element. -
c_locreturns a pointer ofc_ptrtype to aCinteroperable variable, which has eithertargetattribute orpointertype. Likec_ptr,c_locis a generic function that can take anyCinteroperable 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 testRemarks:
-
void *is a generic way inCto declare a pointer that can point to any data type. - Just like
c_ptr,c_funptris a pointer to aCinteroperable function. - Just like
c_loc,c_funlocreturns aCpointer to aCinteroperable function.
-
References
- Interoperability with C (GNU): https://gcc.gnu.org/onlinedocs/gfortran/Interoperability-with-C.html