Ada : Calling Fortran functions from C

Some useful recommendations:

  1. The Fortran 2003 standard has defined a way of interfacing the Fortran procedures with functions 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).
  2. Parameter passing in Fortran is done by address, whereas in C it is done by value.
  3. It is recommended to do the linking with the Fortran compiler. If it is the compiler ifort, -nofor-main option is necessary to indicate that this is not the Fortran main function.

Be aware that if you do not choose the interoperability solution proposed by Fortran 2003, you will have the following restrictions:

  1. When C activates a Fortran procedure, the arguments furnished during the call must be addresses of objects with a pre-defined type.
  2. 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.
  3. As Fortran generates the external references in lower-case letters, this must be respected when making calls in C.

Example of call from C

$ cat appelf.c
#include <stdio.h>
int main ()
{
  int   lg_chaine  = 0;
  float reel       = 0.0;
  char  chaine[]   = "chaîne_C                      ";
 
  lg_chaine = strlen(chaine);
  concat( &amp;lg_chaine, &amp;reel, chaine);
 
  printf("chaîne finale                = %sn", chaine);
  printf("longueur de la chaîne finale = %dn", lg_chaine);
  printf("réel passé par adresse       = %fn", reel);
 
  return 0;
}

Example of definition in Fortran 2003/2008

$ cat concat.f90
module m
  use ISO_C_BINDING
contains
  SUBROUTINE concatenation( lg_chaine, reel, tab_car) bind(C,name='concat')
    INTEGER(kind=C_INT)                          :: lg_chaine
    REAL(kind=C_FLOAT)                           :: reel
    CHARACTER(kind=C_CHAR), dimension(lg_chaine) :: tab_car
    !
    CHARACTER(kind=C_CHAR,len=lg_chaine)         :: chaine
    !write( chaine, '(*(a))' ) tab_car ! Format => Fortran 2008
    ! ou bien
    do i=1,lg_chaine
      chaine(i:i) = tab_car(i)
    end do
    !
    chaine    = TRIM(chaine)//' et chaîne_Fortran'
    lg_chaine = LEN_TRIM(chaine)
    reel      = 100.0
  END SUBROUTINE concatenation
end module m
$ icc -c appelf.c
$ ifort -c concat.f90
$ ifort -nofor-main appelf.o concat.o -o appelc.exe
$ appelc.exe
chaîne finale                = chaîne_C et chaîne_Fortran
longueur de la chaîne finale = 26
réel passé par adresse       = 100.000000

Example of definition in Fortran 90/95

$ cat concat.f90
SUBROUTINE concat( lg_chaine, reel, chaine)
  INTEGER,                  INTENT(INOUT) :: lg_chaine
  REAL,                     INTENT(INOUT) :: reel
  CHARACTER(LEN=lg_chaine), INTENT(INOUT) :: chaine
  chaine    = TRIM(chaine)//' et chaîne_Fortran'
  lg_chaine = LEN_TRIM(chaine)
  reel      = 100.0
END SUBROUTINE concat
$ icc -c appelf.c
$ ifort -c -assume nounderscore concat.f90
$ ifort -nofor-main appelf.o concat.o -o appelc.exe
$ appelc.exe
chaîne finale                = chaîne_C et chaîne_Fortran
longueur de la chaîne finale = 26
réel passé par adresse       = 100.000000