Skip to content

Fortran iso_c_binding

gradcshou edited this page Oct 27, 2015 · 5 revisions

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:

  1. An interface block is created to allow test program to access Basics function because we cannot import a C program to a Fortran program using use statement.
  2. The name statement is used because subroutine name Basics_c is different from function name Basics. It can be omitted if Fortran subroutine name is made to be the same as the C function name.
  3. The only statement specifies the only types required by declaring C function Basics. This is economical way of loading iso_c_binding module. If we are lazy about this, we can simply do use iso_c_binding without only statement.
  4. 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" whereas Fortran program uses "call-by-reference" paradigm.
  5. intent(in) matches const keyword in the variable declaration. But it's not necessary (i.e. the code will still work if we do not use intent(in)).
  6. intent(out) is typically not used because it has conflicts with value attribute.
  7. Because b in the Basics function is declared to be a pointer (i.e. reference) to double, we don't supply value attribute.
  8. Use kind statement to cast a Fortran data type to its corresponding C type or vice versa. A complete list of interoperable data types between C and Fortran 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:

  1. c_ptr is a generic C pointer type that can point to any data type. Here in the example above c_ptr is double *. In C, array essentially can be viewed as a pointer to its first element.
  2. c_loc returns a pointer of c_ptr type to a C interoperable variable, which has either target attribute or pointer type. Like c_ptr, c_loc is a generic function that can take any C 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:

  1. void * is a generic way in C to declare a pointer that can point to any data type.
  2. Just like c_ptr, c_funptr is a pointer to a C interoperable function.
  3. Just like c_loc, c_funloc returns a C pointer to a C interoperable function.
Clone this wiki locally