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}.
crumb trail: > language > C/Fortran interoperability
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 SubroutineAfter 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:
int SomeCFunction(int i,float f) { // this is the actual function } int SomeCFunction_(int i,float f) { return SomeCFunction(i,f); }
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);
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 fdatause 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.
INTEGER,KIND(C_SHORT) :: i
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>and inspecting the output with nm gives:int foo(int x) { return x; }
0000000000000010 s EH_frame1 0000000000000000 T _fooOn the other hand, the identical program compiled as C++ gives
0000000000000010 s EH_frame1 0000000000000000 T __Z3fooiYou 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>you again get the same linker symbols as for C, so that the routine can be called from both C and Fortran.#ifdef __cplusplus extern"C" { #endif int foo(int x) { return x; } #ifdef __cplusplus } #endif
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.
crumb trail: > language > Strings
Programming languages differ widely in how they handle strings.
Example: the main program in Fortran passes a string
Program Fstring character(len=5) :: word = "Word" call cstring(word) end Program Fstring
#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"); }
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.
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.
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.
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.
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 ... ")
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
data = (ctypes.c_int * Nelements)(*[x for x in range(numel)])