Introduction
Description of the Code
Basic Structure and Makefile
The structure adopted here is probably reasonably typical. A block of Fortran code containing data-structures and functions is to be interfaced to a parent program written in C++. Perhaps the easiest part of the project to present is the Makefile
FortCTester: fortran_routines.o cppMain.cpp fortran_routines.h cpp_variables.h
g++ -oFortCTester -g -ggdb -lm -lg2c fortran_routines.o cppMain.cpp
fortran_routines.o: fortran_routines.f fortran_variables.h
g77 -g -ggdb -c fortran_routines.f
clean:
rm FortCTester
rm *.o
Here, the final executable is called FortCTester.
We compile the Fortran code in
fortran_routines.f only to object code (by default in
fortran_routines.o) using the GNU g77 compiler. We have
debugging turned on so that we can have a poke around in the
running program later.
The executable is compiled using the GNU g++ compiler. Because we're linking Fortran code to C++ code, we include the name of the fortran object file in this command. Less obviously, we have to link in an external libg2c library using the -lg2c option.
Based on this Makefile, we can compile our project to a running executable. Of course, a Makefile is only a small part of the process of producing a running program, so we turn our attention next to the Fortran and C++ source code.
C++ Headers
The entire source for the project can be downloaded in the form of a gzipped tarball: FortCTester.tar.gz. The most important parts of the C++ code will be excerpted here.For this example, it makes most sense to begin by discussing the C++ code since it is the main code of the program. In an actual application, often you will begin by reading the Fortran code since the Fortran code will probably have been fully written some time ago, and now you have to repackage it in some way.
As we have seen already, the C++ code is contained in the file cppMain.cpp The first bit of code to give us access to a Fortran function is contained in the included header file fortran_routines.h.
extern "C"
{
extern void fortran_routine__();
}
Here we are saying that there is an function called
fortran_routine__
defined in some other file and we want to be able to access it.
You should note here the use of underscores at the end of the
function name. In fact (as we shall see) the function is
defined in the Fortran code as fortran_function.
However, when a variable/function name appears in the C/C++
namespace it does so with an underscore appended.
(This doesn't apply to all Fortran/C compilers,
but it's a common convention going back to the work of
Stu Feldman). And if the variable/function name already contains
an underscore (like our function does), then it
gets 2 underscores appended. If nothing else, this makes it
easy to see which entities in our namespace are coming from
Fortran-land, and which are native C++.
Getting access to Fortran functions is useful, but it is also necessary to get access to data (especially since we are somewhat limited in our ability to access data through return values of functions ). The included header file cpp_variables.h contains the declarations necessary to gain access to the structures in the Fortran code. To do this, we declare a struct that has the same layout as the Fortran common block we want access to (we'll see this later).
struct variables
{
double value_double;
float value_float;
int value_int;
float value_matrix[4][2];
int value_vector[5];
bool value_bool;
};
Although this looks like any other struct you might
define, it is crucial that its layout is exactly the same as that
of the Fortran common block we will be accessing with it.
To tell our C++ code that we're going to be accessing something foreign using this structure, we use the declaration:
extern "C" {
extern struct variables vari_;
};
Again, the trailing underscore is because vari is coming
from Fortran namespace. We are telling the C++ code to read data
from this foreign entity using the pattern laid down in
variables as defined above.
Fortran Headers/Code
As we can see from the Makefile, all the Fortran code is contained in the file fortran_routine.f and in the header file included into this subroutine include fortran_routine.h (Note that the include process used is a nonstandard, but generally acceptable practice).The most important parts of the Fortran code are contained in include fortran_routine.h. First we must declare all the variables to be used
real*8 value_double
real value_float
integer value_int
real value_matrix(2,4)
integer value_vector(5)
logical value_bool
Then we have to parcel these up into a common block as shown:
common /vari/
+ value_double,
+ value_float,
+ value_int,
+ value_matrix,
+ value_vector,
+ value_bool
save /vari/
Notice that we have listed the variables in the same sequence as
in the C++ struct definition. This is essential,
otherwise you won't get what you want when you read/write to this
data structure.
You should also notice, as a point of good Fortran programming
practice (more of which anon, though I'm no authority)
that the types are listed in decreasing order of size (i.e.
doubles first!). This is useful in
preserving alignment of your data.
Finally, you should be aware that matrices are indexed
differently in C/C++ and in Fortran. The definitions above
illustrate the difference.
The only other point to emphasise is that the names fortran_routine and vari must tally with the names we have used to address these entities in the C++ code (trailing underscores notwithstanding).
Making it Work
At this point, we have set up all the infrastructure needed to get shared access to a data structure between C++ and Fortran, and to call a Fortran function from C++. Calling the Fortran function from C++ is as simple as:fortran_routine__();
Working with the shared variables from Fortran is no big deal, and they are treated like any other variables (that's all they are to Fortran).
value_bool=.TRUE.
value_int=12345
value_float=1.23
value_double=5.67
print*, ' boolean value', value_bool
print*, ' int value', value_int
print*, ' real value', value_float
print*, ' real*8 value', value_double
From C++, we treat our structure like pretty much any other:
vari_.value_bool = true; vari_.value_int = 12345; vari_.value_float = 1.23; vari_.value_double = 5.67;
Mini-Conclusion
However, that is not quite the end of the story, and there is still the potential for some unpleasant hiccups. More of which in Part II.