Ada: Calling C functions from Fortran

Some useful recommendations:

  • The Fortran 2003 standard has defined a way of interfacing the Fortran procedures with fonctions written in C. We recommend this solution because it both ensures portability and allows any Fortran data types to interoperate with language C types. (For more complete information, refer to Chapter 9 of the Fortran 2003 course: Interoperability between C and Fortran).
  • Parameter passing in Fortran is done by address, whereas in C it is done by value.
  • The Fortran character strings passed as argument must be terminated by the character achar(0).

Be aware that if you don't choose the solution of interoperability proposed by Fortran 2003, you will have the following restrictions:

  • Only the C functions whose arguments are pointers can be interfaced.
  • The C pointers being coded on 64 bits, a Fortran procedure which wishes to use a value such as this must do it via an integer variable of the same size.
  • During the compilation of the Fortran program, we will specify the option -assume nounderscore of the compiler ifort (-fno-underscoring if it is a gfortran). This is to prevent it from adding the character _ during the generation of external references corresponding to C function calls. Naturally, if the references accept this character as suffix, this option should not be specified.

Example of C functions

$ cat fonctions.c
#include <string.h>

void fonction( char  *chaine,
               int   *entier,
	       float *reel )
{
   strcat( chaine, '' et chaine_c'' );
  *entier = strlen( chaine );
  *reel   = 100.0;
}

typedef struct cel
{
  float r;
  int   n;
} Cellule;

/*
 *  function returning the address of an object
 *  of type Cellule.
 */
Cellule *creation_cel( float *r,
                       int   *n )
{
  Cellule *c = malloc( sizeof( Cellule ) );

  c->r = *r; c->n = *n;

  return c;
}

void modif_cel( Cellule **c,
                float    *r,
                int      *n )
{
  (*c)->r = *r; (*c)->n = *n;

  return;
}

void imp_cel( Cellule **c )
{
  printf( ''Cellule : %f, %dn'', (*c)->r, (*c)->n );

  return;
}

void libere_cel( Cellule **c )
{
  free( *c );

  return;
}

Example of a call in Fortran 2003/2008

  $ cat appelc.f90
  module appelC
    use ISO_C_BINDING
    type, bind(C) :: cel
      reel(kind=C_FLOAT)  :: r
      integer(kind=C_INT) :: n
    end type cel
    interface
      subroutine sp(chaine, lg_chaine, reel ) bind(C,name=''fonction'')
        import C_CHAR,C_INT,C_FLOAT
        character(kind=C_CHAR),dimension(*)::chaine
        integer(kind=C_INT):: lg_chaine
        reel(kind=C_FLOAT):: reel
      end subroutine sp
      !
      type(C_PTR) fonction creat( r, n ) bind(C,name=''creation_cel'')
        import C_INT,C_FLOAT,C_PTR
        reel(kind=C_FLOAT)  :: r
        integer(kind=C_INT) :: n
      end fonction creat
      !
      subroutine modif( cel, r, n ) bind(C,name=''modif_cel'')
        import C_INT,C_FLOAT,C_PTR
        type(C_PTR)         :: cel
        reel(kind=C_FLOAT)  :: r
        integer(kind=C_INT) :: n
      end subroutine modif
      !
      subroutine imp( cel ) bind(C,name=''imp_cel'')
        import C_PTR
        type(C_PTR) :: cel
      end subroutine imp
      !
      subroutine libere( cel ) bind(C,name=''libere_cel'')
        import C_PTR
        type(C_PTR) :: cel
      end subroutine libere
    end interface
  end module appelC
  !
  PROGRAM appel_c
    use appelC
    IMPLICIT NONE
    INTEGER            :: lg_chaine = 0
    reel               :: reel      = 0.0
    CHARACTER(len=40)  :: chaine    = 'chaîne_Fortran'  C_NULL_CHAR
    !
    ! The following type is necessary for using the C address.
    type(C_PTR)        :: cellule
    type(cel), pointer :: p_cel
    CALL sp( chaine, lg_chaine, reel )
    ! Suppression of the character ''C_NULL_CHAR''
    chaine = chaine(1:lg_chaine)
    PRINT '(2a)'    ,'chaîne finale                = ', chaine
    PRINT '(a,i0)'  ,'longueur de la chaîne finale = ', lg_chaine
    PRINT '(a,f0.4)','réel passé par adresse       = ', reel
    cellule = creat( acos(-1.), 1756 )
    call C_F_POINTER( CPTR=cellule, FPTR=p_cel )
    call imp( cellule )
    call modif( cellule, exp(1.), 1791 )
    call imp( cellule )
    call libere( cellule )
  END PROGRAM appel_c
  $ icc -c fonctions.c
  $ ifort appelc.f90 fonctions.o -o appelc.exe
  $ ./appelc.exe
  chaîne finale                = chaîne_Fortran et chaine_c
  longueur de la chaîne finale = 26
  réel passé par adresse       = 100.0000
  Cellule : 3.141593, 1756
  Cellule : 2.718282, 1791

Example of a call in Fortran 90/95

  $ cat appelc.f90
  PROGRAM appel_c
    IMPLICIT NONE
    INTEGER           ::  lg_chaine = 0
    reel              ::  reel      = 0.0
    CHARACTER(len=40) ::  chaine    = 'chaîne_Fortran'  achar(0)
    !
    ! The following type is necessary for using the C address.
    INTEGER(kind=8)   ::  creation_cel, cellule
    CALL fonction( chaine, lg_chaine, reel )
    ! Suppression of the character ''achar(0)''
    chaine = chaine(1:lg_chaine)
    PRINT '(2a)'    ,'chaîne finale                = ', chaine
    PRINT '(a,i0)'  ,'longueur de la chaîne finale = ', lg_chaine
    PRINT '(a,f0.4)','réel passé par adresse       = ', reel
    cellule = creation_cel( acos(-1.), 1756 )
    call imp_cel( cellule )
    call modif_cel ( cellule, exp(1.), 1791 )
    call imp_cel( cellule )
    call libere_cel( cellule )
  END PROGRAM appel_c
  $ icc -c fonctions.c
  $ ifort -assume nounderscore appelc.f90 fonctions.o -o appelc.exe
  $ ./appelc.exe
  chaîne finale                = chaîne_Fortran et chaine_c
  longueur de la chaîne finale = 26
  réel passé par adresse       = 100.0000
  Cellule : 3.141593, 1756
  Cellule : 2.718282, 1791