Department of Engineering

IT Services

Mixing Languages on linux

If your source code is a mixture of languages, then you need to be careful when making calls from one language to another. Problems arise for a number of reasons

  • function/procedure names - if you write a C++ function called foo you can't call it by that name from Fortran because the C++ compiler changes the routine name - the technical term is "name mangling". Compilers for other languages change the names too.
  • argument call mode - Fortran arguments are always passed "by reference".
  • arrays - fortran arrays begin at 1; C/C++ arrays begin at 0. And C/C++ arrays are stored row by row in memory whereas Fortran stores them column by column
  • argument types - Several problems: The size of integers, floats etc need to match in the 2 languages; Older Fortrans don't support structures; Fortran and C/C++ handle strings differently

Details for these issues are system and compiler dependant. This document will consider some of the simpler issues regarding code generated by gcc, g++ and g77 on linux.

Compiling a Program

When many source files are compiled to create a program there are 2 main stages. Firstly the source files are "compiled" to "object files" (with a .o suffix). You can make the compiler stop after this stage by using the compiler's "-c" switch - e.g.

   gcc -c foo.cc

Then these object files are combined along with some libraries to produce a runnable program. It's only at this stage that a check is done to see if a routine called in one file exists in another.

Several routines and libraries are added at this stage by the compiler. If you're using g77 some Fortran I/O libraries are brought in, but if you're using g++ they won't be, even if there's some fortran code amongst the source files. So you may need to explicitly add some extra system files to the compile line.

To find out what extra files the compiler adds, use the compiler's "-v" (verbose) switch.

C++ calling C

This is the easiest combination. Suppose you've produced this file Cfunction.c

int Cfunction() {
int i =2;
return i;
}

And you write another file named C++main.cc

int Cfunction(); // prototype
int main() {
  int j = Cfunction();
}

If you try compiling using

g++ -c C++main.cc
gcc -c Cfunction.c
g++ -o program C++main.o Cfunction.o

it won't work. You'll get something like

undefined reference to `Cfunction()

The problem is that the C++ compiler assumes that Cfunction is a C++ routine and calls a mangled version of the name - something like _Z9Cfunctionv. You have to tell the compiler that the external function has been compiled with a C compiler. If you change C++main.cc to the following, the compilation should work

extern "C" {
int Cfunction(); // prototype
}"

int main() {
  int j = Cfunction();
}

C++ calling Fortran function without arguments

Put the following code into ifunction.f

      function ifunction()
      ifunction=1
      return
      end

and the following code into C++Fmain.cc

// Put extern "C" here to avoid name-mangling. Alas, 
// extern "Fortran" is unavailable.
// Note that g77 adds an underscore to names so C++
// has to add an underscore to the fortran function name!
extern "C" {
int ifunction_();
}
#include <iostream>
int main() {
  int j=ifunction_();
  std::cout <<j  << std::endl;
}

Compile using

g++ -c C++Fmain.cc
g77 -c ifunction.f
g++ -o program C++Fmain.o ifunction.o

Now the program should run, printing 1.

C++ calling Fortran function with arguments

Put the following code into ifunctionargs.f

      function ifunctionargs(i)
      ifunctionargs=i*2
      return
      end

and the following code into C++Fargsmain.cc

// Put extern "C" here to avoid name-mangling. Alas, 
// extern "Fortran" is unavailable.
// Note that g77 adds an underscore to names so C++
// has to add an underscore to the fortran function name!
extern "C" {
int ifunctionargs_(int&);
}
#include <iostream>
int main() {
  int i=2;
  int j=ifunctionargs_(i);
  std::cout >> j >> std::endl;
}

Compile using

g++ -c C++Fargsmain.cc
g77 -c ifunctionargs.f
g++ -o program C++Fargsmain.o ifunctionargs.o

Now the program should run, printing the answer 4. Note that ifunctionargs is declared to take a reference to an integer. This is so the compiler passes the integer in a form that fortran requires.

Fortran procedures are called much as functions are.

Fortran calling C

The fortran compiler will add an underscore to the names of called functions, including external ones. It will pass arguments as pointers. Put the following code into f77callingC.f

      program fcallc
      i=4
      CALL doubling(i)
      PRINT *,i
      stop
      end

and this into cfunction.c

void doubling_(int* i) {
  int j=*i;
  *i= j*2;
}
Compile using
gcc -c cfunction.c
g77 -c f77callingC.f
g77 -o program cfunction.o f77callingC.o

Running the program should produce the output "8".

Fortran calling C with string arguments

Put the following code into fcallc.f

      program fcallc
      CALL stringing('hello!')
      stop
      end

and this into stringing.c

void stringing_(char* s, int len) {
char *str = (char*)malloc(len+1);
strncpy(str,s,len);
str[len]='\0';
printf("%s\n",str);
}

Compile using

gcc -c stringing.c
g77 -c fcallc.f
g77 -o program stringing.o fcallc.o

If you run the program, "hello!" should be printed out. For each string that fortran passes, it adds a hidden integer to the end of the argument list. The C program needs to know the length because the string isn't null-terminated. The first thing that this C program does is to create a null-terminated copy of the string. Note that if the fortran call used 2 strings, the C routine would receive 2 character pointers followed by 2 integers.

See Also