Jean Zay : appel des fonctions C à partir de Fortran

Quelques recommandations utiles :

  1. La norme 2003 de Fortran a défini une manière d'interfacer des procédures Fortran avec des fonctions écrites en C. On recommande cette solution car elle a l'avantage d'une part d'assurer la portabilité et d'autre part de permettre la mise en relation de tout type Fortran avec ceux du langage C.
    Pour de plus amples informations, se reporter au chapitre 9 du cours Fortran 2003 : Interopérabilité entre entités C et Fortran.
  2. Le passage des paramètres en Fortran s'effectue par adresse, alors qu'en C la transmission se fait par valeur ;
  3. Les chaînes de caractères Fortran passées en arguments doivent être bornées par le caractère achar(0) ;

Sachez que si vous n'adoptez pas la solution proposée par Fortran 2003, vous vous exposez aux restrictions suivantes :

  1. seules les fonctions C dont les arguments sont des pointeurs peuvent être interfacées ;
  2. les pointeurs C étant codés sur 64 bits, une procédure Fortran qui désire récupérer une telle valeur devra le faire via une variable entière de même gabarit ;
  3. lors de la compilation du programme Fortran on précisera l'option -assume nounderscore du compilateur ifort (-fno-underscoring s'il s'agit de gfortran) afin que celui-ci n'ajoute pas le caractère _ lors de la génération des références externes correspondant aux appels des fonctions C. Évidemment, si celles-ci admettent ce caractère comme suffixe, cette option ne devra pas être indiquée.

Exemple de fonctions C

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;
 
/*
 *  Fonction retournant l'adresse d'un objet
 *  de 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, %d\n", (*c)->r, (*c)->n );
 
  return;
}
 
void libere_cel( Cellule **c )
{
  free( *c );
 
  return;
}

Exemple d'appel en Fortran 2003/2008

appelc.f90
module appelC
  use ISO_C_BINDING, only : C_CHAR, C_INT, C_FLOAT, C_PTR
  type, bind(C) :: cel
    real(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
      real(kind=C_FLOAT)                  :: reel
    end subroutine sp
    !
    type(C_PTR) function creat( r, n ) bind(C,name='creation_cel')
      import C_INT, C_FLOAT, C_PTR
      real(kind=C_FLOAT)  :: r
      integer(kind=C_INT) :: n
    end function creat
    !
    subroutine modif( cel, r, n ) bind(C,name='modif_cel')
      import C_INT, C_FLOAT, C_PTR
      type(C_PTR)         :: cel
      real(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 ISO_C_BINDING, only : C_PTR, C_F_POINTER, C_NULL_CHAR
  use appelC
  IMPLICIT NONE
  INTEGER            :: lg_chaine = 0
  REAL               :: reel      = 0.0
  CHARACTER(len=40)  :: chaine    = 'chaîne_Fortran'//C_NULL_CHAR
  !
  ! Type suivant nécessaire pour la récupération d'adresse C.
  type(C_PTR)        :: cellule
  type(cel), pointer :: p_cel
  CALL sp( chaine, lg_chaine, reel )
  ! Suppression du caractère "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

Exemple d'appel en Fortran 90/95

appelc.f90
PROGRAM appel_c
  IMPLICIT NONE
  INTEGER           ::  lg_chaine = 0
  REAL              ::  reel      = 0.0
  CHARACTER(len=40) ::  chaine    = 'chaîne_Fortran'//achar(0)
  !
  ! Type suivant nécessaire pour la récupération d'adresse C.
  INTEGER(kind=8)   ::  creation_cel, cellule
  CALL fonction( chaine, lg_chaine, reel )
  ! Suppression du caractère "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