Some Examples in Mixed Language Programming
Example 1: Main Program in C, with Subroutines in C, C++, and Fortran
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.
#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;
}
subroutine ffunction(a,b)
a=3.0
b=4.0
end
extern "C" {
void cppfunction(float *a, float *b);
}
void cppfunction(float *a, float *b) {
*a=5.0;
*b=6.0;
}
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:
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:
Example 2: Main Program in C++, with Subroutines in C, C++, and Fortran
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.
#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;
}
void cfunction(float *a, float *b) {
*a=7.0;
*b=8.0;
}
void cppfunction(float *a, float *b) {
*a=5.0;
*b=6.0;
}
subroutine ffunction(a,b)
a=3.0
b=4.0
end
At the command line:
Running the program, we get:
Example 3: Main Program in Fortran, with Subroutines in C, C++, and Fortran
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.
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
subroutine ffunction(a,b)
a=3.0
b=4.0
end
extern "C" {
void cppfunction_(float *a, float *b);
}
void cppfunction_(float *a, float *b) {
*a=5.0;
*b=6.0;
}
void cfunction_(float *a, float *b) {
*a=7.0;
*b=8.0;
}
Back at the command line:
Running the program, we get:
References
- "Mixing Code in C, C++, and FORTRAN on Unix." Computer Aided Engineering Network. Tennessee Technological University.source
- Metcalf, Michael. Modern Fortran Explained. Oxford: Oxford University Press, 2011. 243-254.
- Clerman, Norman. Modern Fortran: Style and Usage. New York: Cambridge University Press, 2012. 262-270.