C/Fortran interoperability

Experimental html version of downloadable textbook, see https://www.tacc.utexas.edu/~eijkhout/istc/istc.html
\[ % mathjax inclusion. \newcommand\inv{^{-1}}\newcommand\invt{^{-t}} \newcommand\bbP{\mathbb{P}} \newcommand\bbR{\mathbb{R}} \newcommand\defined{ \mathrel{\lower 5pt \hbox{${\equiv\atop\mathrm{\scriptstyle D}}$}}} \newcommand\macro[1]{$\langle$#1$\rangle$} \newcommand\dtdxx{\frac{\alpha\Delta t}{\Delta x^2}} \] 31.1 : C/Fortran interoperability
31.1.1 : Linker conventions
31.1.2 : Complex numbers
31.1.3 : C bindings in Fortran 2003
31.2 : C/C++ linking
31.3 : Strings
31.4 : Subprogram arguments
31.5 : Input/output
31.6 : Python calling C code
Back to Table of Contents

31 C/Fortran interoperability

Most of the time, a program is written is written in a single language, but in some circumstances it is necessary or desirable to mix sources in more than one language for a single executable. One such case is when a library is written in one language, but used by a program in another. In such a case, the library writer will probably have made it easy for you to use the library; this section is for the case that you find yourself in the place of the library writer. We will focus on the common case of interoperability between C/C++ and Fortran.

This issue is complicated by the fact that both languages have been around for a long time, and various recent language standards have introduced mechanisms to facilitate interoperability. However, there is still a lot of old code around, and not all compilers support the latest standards. Therefore, we discuss both the old and the new solutions.

For the issues of arrays, see chapter~\ref{tut:array}.

31.1 C/Fortran interoperability

crumb trail: > language > C/Fortran interoperability

31.1.1 Linker conventions

crumb trail: > language > C/Fortran interoperability > Linker conventions

As explained above, a compiler turns a source file into a binary, which no longer has any trace of the source language: it contains in effect functions in machine language. The linker will then match up calls and definitions, which can be in different files. The problem with using multiple languages is then that compilers have different notions of how to translate function names from the source file to the binary file.

Let's look at codes (you can find example files in tutorials/linking ):

// C:

void foo() {

  return;

}

! Fortran

      Subroutine foo()

      Return

      End Subroutine

After compilation you can use \indexterm{nm} to investigate the binary object file:

%% nm fprog.o

0000000000000000 T _foo_

....

%% nm cprog.o

0000000000000000 T _foo

....

You see that internally the foo routine has different names: the Fortran name has an underscore appended. This makes it hard to call a Fortran routine from C, or vice versa. The possible name mismatches are:

Since C is a popular language to write libraries in, this means that the problem is often solved in the C library by:

31.1.2 Complex numbers

crumb trail: > language > C/Fortran interoperability > Complex numbers

The complex data types in C/C++ and Fortran % other. Here is an example of a C++ program linking to Lapack's complex vector scaling routine \indextermtt{zscal}.

// zscale.cxx

extern "C" {

void zscal_(int*,double complex*,double complex*,int*);

}

  complex double *xarray,*yarray, scale=2.;

  xarray = new double complex[n]; yarray = new double complex[n];

  zscal_(&n,&scale,xarray,&ione);

31.1.3 C bindings in Fortran 2003

crumb trail: > language > C/Fortran interoperability > C bindings in Fortran 2003

With the latest Fortran standard there are explicit C bindings making it possible to declare the external name of variables and routines:

module operator

  real, bind(C) :: x

contains

  subroutine s() bind(C,name='s')

  return

  end subroutine

end module

%% ifort -c fbind.F90

%% nm fbind.o

.... T _s

.... C _x

It is also possible to declare data types to be C-compatible:

Program fdata

use iso_c_binding

type, bind(C) :: c_comp real (c_float) :: data integer (c_int) :: i type (c_ptr) :: ptr end type

end Program fdata

The latest version of Fortran, unsupported by many compilers at this time, has mechanisms for interfacing to C.

31.2 C/C++ linking

crumb trail: > language > C/C++ linking

Libraries written in C++ offer further problems. The C++ compiler makes external symbols by combining the names a class and its methods, in a process known as name mangling \index{C++!name mangling}. You can force the compiler to generate names that are intelligible to other languages by

#ifdef __cplusplus

  extern"C" {

#endif

  .

  .

  place declarations here

  .

  .

#ifdef __cplusplus

  }

#endif

Example: compiling

#include <stdlib.h>

int foo(int x) { return x; }

and inspecting the output with nm gives:

0000000000000010 s EH_frame1

0000000000000000 T _foo

On the other hand, the identical program compiled as C++ gives

0000000000000010 s EH_frame1

0000000000000000 T __Z3fooi

You see that the name for foo is something mangled, so you can not call this routine from a program in a different language. On the other hand, if you add the extern declaration:

#include <stdlib.h>

#ifdef __cplusplus extern"C" { #endif int foo(int x) { return x; } #ifdef __cplusplus } #endif

you again get the same linker symbols as for C, so that the routine can be called from both C and Fortran.

If your main program is in C, you can use the C++ compiler as linker. If the main program is in Fortran, you need to use the Fortran compiler as linker. It is then necessary to link in extra libraries for the C++ system routines. For instance, with the Intel compiler -lstdc++ -lc needs to be added to the link line.

The use of extern is also needed if you link other languages to a C++ main program. For instance, a Fortran subprogram foo should be declared as

extern "C" {

void foo_();

}

In that case, you again use the C++ compiler as linker.

31.3 Strings

crumb trail: > language > Strings

Programming languages differ widely in how they handle strings.

As you can see, passing strings between different languages is fraught with peril. This situation is made even worse by the fact that passing strings as subroutine arguments is not standard.

Example: the main program in Fortran passes a string

Program Fstring

  character(len=5) :: word = "Word"

  call cstring(word)

end Program Fstring

and the C routine accepts a character string and its length:
#include <stdlib.h>

#include <stdio.h>

void cstring_(char *txt,int txtlen) { printf("length = %d\n",txtlen); printf("<<"); for (int i=0; i<txtlen; i++) printf("%c",txt[i]); printf(">>\n"); }

which produces:

length = 5

<<Word >>

To pass a Fortran string to a C program you need to append a null character:

call cfunction ('A string'//CHAR(0))

Some compilers support extensions to facilitate this, for instance writing

DATA forstring /'This is a null-terminated string.'C/

Recently, the `C/Fortran interoperability standard' has provided a systematic solution to this.

31.4 Subprogram arguments

crumb trail: > language > Subprogram arguments

In C, you pass a float argument to a function if the function needs its value, and float* if the function has to modify the value of the variable in the calling environment. Fortran has no such distinction: every variable is passed \indexterm{by reference}. This has some strange consequences: if you pass a literal value

37 to a subroutine, the compiler will allocate a nameless variable with that value, and pass the address of it, rather than the value\footnote{With a bit of cleverness and the right compiler, you can have a program that says print *,7 and prints  8 because of this.}.

For interfacing Fortran and C routines, this means that a Fortran routine looks to a C program like all its argument are `star' arguments. Conversely, if you want a C subprogram to be callable from Fortran, all its arguments have to be star-this or that. This means on the one hand that you will sometimes pass a variable by reference that you would like to pass by value.

Worse, it means that C subprograms like

void mysub(int **iarray) {

 *iarray = (int*)malloc(8*sizeof(int));

 return;

}

can not be called from Fortran. There is a hack to get around this (check out the Fortran77 interface to the Petsc routine

VecGetValues ) and with more cleverness you can use

POINTER variables for this.

31.5 Input/output

crumb trail: > language > Input/output

Both languages have their own system for handling input/output, and it is not really possible to meet in the middle. Basically, if Fortran routines do I/O, the main program has to be in Fortran. Consequently, it is best to isolate I/O as much as possible, and use C for I/O in mixed language programming.

31.6 Python calling C code

crumb trail: > language > Python calling C code

Because of its efficiency of computing, C is a logical language to use for the lowest layers of a program. On the other hand, because of its expressiveness, Python is a good candidate for the top layers. It is then a logical thought to want to call C routines from a python program. This is possible using the python ctypes \index{ctypes (python module)} module.

  1. indicated above;
  2. \indextermtt{libc}:

    path_libc = ctypes.util.find_library("c")
    
    libc = ctypes.CDLL(path_libc)
    
    libc.printf(b"%s\n", b"Using the C printf function from Python ... ")
    
    

  3. test_add = mylib.test_add
    
    test_add.argtypes = [ctypes.c_float, ctypes.c_float]
    
    test_add.restype = ctypes.c_float
    
    test_passing_array = mylib.test_passing_array
    
    test_passing_array.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.c_int]
    
    test_passing_array.restype = None
    
    

  4. data = (ctypes.c_int * Nelements)(*[x for x in range(numel)])
    
    

Back to Table of Contents