[[introduction]]
Introduction
~~~~~~~~~~~~
Anybody working in the area of numerical programming and engineering
software (as opposed to software engineering!) will more than likely
have to work with Fortran code. A common procedure now is to integrate
such legacy Fortran code into a more modern framework. In the course of
my work, I came into contact with quite a large body of Fortran and C++
code. To better understand the interface between the two, I studied in
some detail a basic sample application that shows the main ideas related
to the construction of such code. I also came into contact with a subtle
bug in the course of this process, highlighting some of the problems
that can arise when mixing languages.
[[description-of-the-code]]
Description of the Code
~~~~~~~~~~~~~~~~~~~~~~~
[[basic-structure-and-makefile]]
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]]
C++ Headers
^^^^^^^^^^^
The entire source for the project can be downloaded in the form of a
link:/downloads/FortCTester.tar.gz[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.
(http://www.math.utah.edu/software/c-with-fortran.html#routine-naming[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
http://www.chiralcomp.com/support/mixing_f77_c_cpp/[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
http://www.math.utah.edu/software/c-with-fortran.html#function-return-types[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-headerscode]]
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,
http://www.math.utah.edu/software/c-with-fortran.html[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
http://www.ibiblio.org/pub/languages/fortran/ch5-3.html[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]]
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:
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]]
Mini-Conclusion
~~~~~~~~~~~~~~~
And that, as they say, is that. We have successfully attached Fortran
code to a C++ program, and got the two of them talking to one another.
The code in the tarfile essentially goes through the motions of setting
up the arrangement described above and writing to data-structures from
C++. It then calls a Fortran routine that prints out the values (to make
sure they are recorded correctly) and rewriting the values. When the
Fortran portion finishes, C++ then prints out the Fortran-written
variables, closing the circle and showing that we have good
communication between these two languages.
However, that is not quite the end of the story, and there is still the
potential for some unpleasant hiccups. More of which in
link:/reprint/fortran_and_cplusplus_part2[Part II].
>> Home