Isaac
Contrasting Crescents
Mixed Language Programming
I have no reason to believe that the human intellect is able to weave a system of physics out of its own resources without experimental labor. Whenever the attempt had been made it has resulted in an unnatural and self-contradictory mass of rubbish.James Clerk Maxwell

Why are programming languages mixed nowadays? To give an example, one might wish to do heavy numerical calculatons in Fortran, have a graphical interface to that program written in C++, and have other system functions performed in C. This way the individual tasks of your project are executed in the language best suited for each of them.

Fortran is the primary language for some of the most intensive supercomputing tasks. However, since the late 1990s, almost all of the most widely used support libraries have been written in C and, more often, C++. Consequently, a growing fraction of scientific code is also written in these languages. For this reason, facilities for interoperation with C were added to Fortran 2003, and enhanced by ISO/IEC technical specification 29113, which was incorporated into Fortran 2015.

The major idea to keep in mind when working through the examples below is the concept of call by value versus call by reference. C and C++ default to passing arguments by value whereas Fortran defaults to passing arguments by reference. In other words, the normal way that Fortran subroutines and functions are called allows them to modify their arguments inside the procedure. C and C++ procedures use a slightly different syntax to allow for modification of arguments. This syntax is used in these examples, which were performed in a Unix/Linux environment.

Contents

  1. Main Program in C, with Subroutines in C, C++, and FortranDownload the files for Example 1
  2. Main Program in C++, with Subroutines in C, C++, and FortranDownload the files for Example 2
  3. Main Program in Fortran, with Subroutines in C, C++, and FortranDownload the files for Example 3

Some Examples in Mixed Language Programming

Example 1: Main Program in C, with Subroutines in C, C++, and Fortran

Elijah

C requires that we use a "call by reference" syntax to make the changes in this example persistent, rather than its default "call by value" method. Note that the name of the Fortran subroutine called from the C program is ffunction_, a name we extracted via the Unix/Linux nm command. Note also that the C++ function has an extern "C" directive above the code of the function, indicating not that cppfunction() is written in C, but that it is called from a C-style interface instead of a C++ interface.

cprogram.c

Elijah
#include <stdio.h>
int main(void) {
  float a=1.0, b=2.0;
  printf("Before running Fortran function:\n");
  printf("a=%f\n",a);
  printf("b=%f\n",b);
  ffunction_(&a,&b);
  printf("After running Fortran function:\n");
  printf("a=%f\n",a);
  printf("b=%f\n",b);
  printf("Before running C++ function:\n");
  printf("a=%f\n",a);
  printf("b=%f\n",b);
  cppfunction(&a,&b);
  printf("After running C++ function:\n");
  printf("a=%f\n",a);
  printf("b=%f\n",b);
  printf("Before running C function:\n");
  printf("a=%f\n",a);
  printf("b=%f\n",b);
  cfunction(&a,&b);
  printf("After running C function:\n");
  printf("a=%f\n",a);
  printf("b=%f\n",b);
  return 0;
}
        

ffunction.f

Elijah
subroutine ffunction(a,b)
a=3.0
b=4.0
end
        

cppfunction.C

Elijah
extern "C" {
  void cppfunction(float *a, float *b);
}
void cppfunction(float *a, float *b) {
  *a=5.0;
  *b=6.0;
}
        

cfunction1.c

Elijah
void cfunction(float *a, float *b) {
  *a=7.0;
  *b=8.0;
}
        

Each program is compiled into an object file using the appropriate compiler with the -c flag. After all the object files are created, the final gcc command links the object files together into a single executable:

gcc -c cprogram.c
gfortran -c ffunction.f
g++ -c cppfunction.C
gcc -c cfunction1.c
gcc -o cprogram cprogram.o ffunction.o cppfunction.o cfunction1.o
        

Though this example does not require it, intrinsic mathematical functions require that you also link in the libm math library. (To do this, add the -lm flag to the final gcc command above.) Results:

./cprogram
Before running Fortran function:
a=1.000000
b=2.000000
After running Fortran function:
a=3.000000
b=4.000000
Before running C++ function:
a=3.000000
b=4.000000
After running C++ function:
a=5.000000
b=6.000000
Before running C function:
a=5.000000
b=6.000000
After running C function:
a=7.000000
b=8.000000
        

Example 2: Main Program in C++, with Subroutines in C, C++, and Fortran

Elijah

The only difference between the C++ code of a "normal" program compared to the current example is that we have to account for the C and Fortran procedures expecting to be called with a C-style interface, and also that the Fortran compiler, gfortran, will append an underscore to the Fortran subroutine name.

cppprogram.C

Elijah
#include <iostream.h>
extern "C" {
  void ffunction_(float *a, float *b);
}
extern "C" {
  void cfunction(float *a, float *b);
}
void cppfunction(float *a, float *b);
int main() {
  float a=1.0, b=2.0;
  cout << "Before running Fortran function:" << endl;
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  ffunction_(&a,&b);
  cout << "After running Fortran function:" << endl;
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  cout << "Before running C function:" << endl;
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  cfunction(&a,&b);
  cout << "After running C function:" << endl;
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  cout << "Before running C++ function:" << endl;
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  cppfunction(&a,&b);
  cout << "After running C++ function:" << endl;
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  return 0;
}
        

cfunction1.c

Elijah
void cfunction(float *a, float *b) {
  *a=7.0;
  *b=8.0;
}
        

cppfunction1.C

Elijah
void cppfunction(float *a, float *b) {
  *a=5.0;
  *b=6.0;
}
        

ffunction.f

Elijah
subroutine ffunction(a,b)
a=3.0
b=4.0
end
        

At the command line:

g++ -c cppprogram.C
gcc -c cfunction1.c
g++ -c cppfunction1.C
gfortran -c ffunction.f
g++ -o cppprogram cppprogram.o cfunction1.o cppfunction1.o ffunction.o
        

Running the program, we get:

./cppprogram
Before running Fortran function:
a=1
b=2
After running Fortran function:
a=3
b=4
Before running C function:
a=3
b=4
After running C function:
a=7
b=8
Before running C++ function:
a=7
b=8
After running C++ function:
a=5
b=6
        

Example 3: Main Program in Fortran, with Subroutines in C, C++, and Fortran

Elijah

Though the non-Fortran procedures don't have underscores after their names in the main Fortran program, running the nm command on fprogram.o shows that the Fortran program expects to have underscores appended to the function names internally. Therefore, in both the C and C++ files, we need to write the function prototype statements accordingly, with an underscore after each function name. We must also use the extern "C" directive in the C++ file, indicating that the C++ function is called with a C-style interface, similar to the first example. Note also that Fortran 2003 provides an ISO C Binding module, declared with use, intrinsic :: ISO_C_binding, which specifies to the Fortran compiler that C calling conventions should be used. This will make the code more portable for multi-language projects.

fprogram.f

Elijah
program fprogram
use, intrinsic :: ISO_C_binding
real a,b
a=1.0
b=2.0
print*,'Before Fortran function is called:'
print*,'a=',a
print*,'b=',b
call ffunction(a,b)
print*,'After Fortran function is called:'
print*,'a=',a
print*,'b=',b
print*,'Before C function is called:'
print*,'a=',a
print*,'b=',b
call cfunction(a,b)
print*,'After C function is called:'
print*,'a=',a
print*,'b=',b
print*,'Before C++ function is called:'
print*,'a=',a
print*,'b=',b
call cppfunction(a,b)
print*,'After C++ function is called:'
print*,'a=',a
print*,'b=',b
stop
end
        

ffunction.f

Elijah
subroutine ffunction(a,b)
a=3.0
b=4.0
end
        

cppfunction2.C

Elijah
extern "C" {
  void cppfunction_(float *a, float *b);
}
void cppfunction_(float *a, float *b) {
  *a=5.0;
  *b=6.0;
}
        

cfunction2.c

Elijah
void cfunction_(float *a, float *b) {
  *a=7.0;
  *b=8.0;
}
        

Back at the command line:

gfortran -c fprogram.f
gfortran -c ffunction.f
g++ -c cppfunction2.C
gcc -c cfunction2.c
gfortran -lc -o fprogram fprogram.o ffunction.o cppfunction2.o cfunction2.o
        

Running the program, we get:

./fprogram
Before Fortran function is called:
a=  1.
b=  2.
After Fortran function is called:
a=  3.
b=  4.
Before C function is called:
a=  3.
b=  4.
After C function is called:
a=  7.
b=  8.
Before C++ function is called:
a=  7.
b=  8.
After C++ function is called:
a=  5.
b=  6.
        

References

Elijah
back-to-top DANGER