Making C and C++ functions callable from Fortran 77


We have learnt that (for many fortran compilers, but not HP Unix f77) you must end your front end function (wrapper) names with an underscore (_). This makes it possible for the fortran compiler to link to your non-fortran functions. The rules about argument passing still applies. For strings this means that fortran will pass you the string lengths which must be accepted in the function argument list. The string should be treated carefully, since fortran strings do not have a ‘\0’ at the end (see examples below).


Example 1


 

Assume that you have a C or C++ function void event(long,char*) which you want to make callable from fortran. This can be obtained by defining a C/C++ front end function as described below.

C/C++ front end function

extern “C” /* this line should be omitted in C */
{
void event_(double *energy, char *in_name, int in_name_length) {
// must take care of the string since fortran does not provide a
// \0 at the end of the string.
char* name=new char[in_name_length+1]; // C++ memory allocation
for (unsigned int i=0; i<in_name_length; i++)
name[i]=in_name[i]; // strncpy is probably nicer here
name[in_name_length]=’\0′;

/* normal C/C++ programming here */
event(*energy,name);
/* which you have defined elsewhere as void event(double,char*) */

delete[] name; // C++ memory cleanup
}
}

/* Need a small wrapper for the fortran main program, since fortran
code should not be used as the main program, see information
elsewhere */
extern “C” int mymain_();
int main()
{
return mymain_();
}

where we assume event(double,char*) is defined elsewhere. Note that, the front end function event_ and the function we want to reach, event, are two different functions. The front end function is called from fortran as:

Fortran

SUBROUTINE MYMAIN

CHARACTER*11 NAME
ECM=91.2
NAME=’CHIRAL DATA’
CALL EVENT(ECM,NAME)

RETURN
END
Note 1: There is no underscore in the fortran code, the fortran compiler provides one automatically (for almost all compilers), and the string length is not explicitly passed.

Note 2: You could of course define your function directly according to the front end function rules.

Compiling and linking

The example here applies for GNU’s C, C++, and fortran compilers (the examples are tried out with these, but should work similarly for other compilers). The fortran code is compiled with

f77 -c test_f.f
and the C++ code is compiled with
g++ -c test_c++.cc
Here we assume that the C++ code is stored in file test_c++.cc, and the fortran code resides in file test_f.f. The two commands above generates two files test_c++.o and test_f.o, and should be linked with the external function event. For this example we assume that event is pre-compiled in a object file called external.o.
Following the information in the Linking C, C++, and Fortran 77 page, we find out that the fortran runtime support is located in library f2c (g2c for later ecgs compilers). So issuing the link command

g++ -o test_run test_f.o test_c++.o external.o -lf2c
will produce a binary test_run.


Example 2


Here we provide a fully working example. Following the recipe here should create a binary that produces the following output

Hello

You passed the following to me:
long integer: 42
string: HELLO WORLD
string length: 11
Create file test_c++.cc with the following content

#include <iostream>

extern “C” /* this line should be omitted in C */
{
void hello_(long *integ, char *in_str, int in_str_length) {
// must take care of the string since fortran does not provide a
// \0 at the end of the string.
char* str=new char[in_str_length+1]; // C++ memory allocation
for (unsigned int i=0; i<in_str_length; i++)
str[i]=in_str[i]; // strncpy is probably nicer here
str[in_str_length]=’\0′;

// normal C/C++ programming here
cout << “Hello\n\n”;
cout << “You passed the following to me:\n”;
cout << “long integer: ” << *integ << ‘\n’;
cout << “string: ” << str << ‘\n’;
cout << “string length: ” << in_str_length << “\n”;

delete[] str; // C++ memory cleanup
}
}

/* Need a small wrapper for the fortran main program, since fortran
code should not be used as the main program, see information
elsewhere */
extern “C” int mymain_();
int main()
{
return mymain_();
}
And another file test_f.f with the content:

SUBROUTINE MYMAIN

CHARACTER*11 NAME

INTEG=42
NAME=’HELLO WORLD’
CALL HELLO(INTEG,NAME)

RETURN
END
These two files are compiled with

g++ -c test_c++.cc
and
f77 -c test_f.f
respectively. Linking is done with
g++ -o test_run test_f.o test_c++.o -lf2c
This creates a binary test_run that produces the output above.