Fortran & C Interoperability (Part 2)

CHere’s another example using the C interoperability features of modern Fortran. First introduced in Fortran 2003, this allows for easily calling C routines from Fortran (and vice versa) in a standard and portable way. Further interoperability features will also be added in the next edition of the standard.

For a Fortran user, strings in C are pretty awful (like most things in C). This example shows how to call a C function that returns a string (in this case, the dlerror function).


module get_error_module

use, intrinsic :: iso_c_binding

implicit none

private

public :: get_error

interface
  !interfaces to C functions
  function dlerror() result(error) &
      bind(C, name='dlerror')
      import
      type(c_ptr) :: error
  end function dlerror
  function strlen(str) result(isize) &
      bind(C, name='strlen')
      import
      type(c_ptr),value :: str
      integer(c_int) :: isize
  end function strlen   
end interface

contains

function get_error() result(error_message)
  !! wrapper to C function char *dlerror(void);

  character(len=:),allocatable :: error_message

  type(c_ptr) :: cstr
  integer(c_int) :: n

  cstr = dlerror() ! pointer to C string

  if (c_associated(cstr)) then

    n = strlen(cstr) ! get string length

    block
      !convert the C string to a Fortran string
      character(kind=c_char,len=n+1),pointer :: s
      call c_f_pointer(cptr=cstr,fptr=s)
      error_message = s(1:n)
      nullify(s)
    end block

  else
    error_message = ''
  end if

end function get_error

end module get_error_module

First we define the bindings to two C routines so that we can call them from Fortran. This is done using the INTERFACE block. The main one is the dlerror function itself, and we will also use the C strlen function for getting the length of a C string. The bind(C) attribute indicates that they are C functions. The get_error function first calls dlerror, which returns a variable of type(c_ptr), which is a C pointer. We use c_f_pointer to cast the C pointer into a Fortran character string (a CHARACTER pointer variable with the same length). Note that, after we know the string length, the block construct allows us to declare a new variable s of the length we need (this is a Fortran 2008 feature). Then we can use it like any other Fortran string (in this case, we assign it to error_message, the deferred-length string returned by the function).

Of course, Fortran strings are way easier to deal with, especially deferred-length (allocatable) strings, and don’t require screwing around with pointers or '\0' characters. A few examples are given below:

subroutine string_examples()

  implicit none

  !declare some strings:
  character(len=:),allocatable :: s1,s2 

  !string assignment:
  s1 = 'hello world' !set the string value
  s2 = s1 !set one string equal to another

  !string slice:
  s1 = s1(1:5) !now, s1 is 'hello'

  !string concatenation:
  s2 = s1//' '//s1 ! now, s2 is 'hello hello'

  !string length:
  write(*,*) len(s2) ! print length of s2 (which is now 11)

  !and there are no memory leaks,
  !since the allocatables are automatically
  !deallocated when they go out of scope.

end subroutine string_examples

See also

Tagged with: , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

*