Linking Fortran and C++ binaries using gcc
You're looking for g++ main.o print_hi.o -o main -lgfortran
to link in the standard Fortran libraries.
You can also use gfortran
by passing -lstdc++
.
How to properly link gfortran and gcc?
Firstly, your C program testigrf.c
is defective. As it is,
you have the warning:
testigrf.c: In function ‘main’:
testigrf.c:23:5: warning: implicit declaration of function ‘igrf12syn_’ [-Wimplicit-function-declaration]
igrf12syn_(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
which occurs because your have declared a function called igrf12syn
and invoked
one called igrf12syn_
, about which the compiler knows nothing.
Change igrf12syn_
to igrf12syn
, recompile, and then the errors revealed are:
testigrf.c: In function ‘main’:
testigrf.c:23:26: warning: passing argument 3 of ‘igrf12syn’ from incompatible pointer type [-Wincompatible-pointer-types]
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘int *’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:44: error: incompatible type for argument 7 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:46: error: incompatible type for argument 8 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:48: error: incompatible type for argument 9 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
testigrf.c:23:50: error: incompatible type for argument 10 of ‘igrf12syn’
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
^
testigrf.c:3:14: note: expected ‘double *’ but argument is of type ‘double’
extern void igrf12syn( int *isv, double *year, double *itype, double *alt,
^
Read the diagnostics and fix the errors before you go any further. They
might be fixed, for example, by changing:
double r, th, ph,year,alt2,lat,colat,elong,x,y,z,f;
to
double r, th, ph,year,alt2,lat,colat,elong,x,y,z,f,itype;
and changing:
igrf12syn(&isv,&year,&isv,&r, &th, &ph,x,y,z,f);
to:
igrf12syn(&isv,&year,&itype,&r, &th, &ph,&x,&y,&z,&f);
That will satisfy the compiler, though it may not express your intentions,
which are not clear to me.
You understand that you cannot link two programs into a single program,
since the linker will find duplicate main
functions. You need to link
the C main program just with the Fortran subroutine igrf12syn
.
Next, download and save the file https://www.ngdc.noaa.gov/IAGA/vmod/igrf12.f
.
Open it in a text/programming editor. Select all the of lines - the complete
lines, including leading space - that compose the subroutine igrf12syn
, from:
subroutine igrf12syn (isv,date,itype,alt,colat,elong,x,y,z,f)
to:
end
Save this selection exactly as copied in a new file igrf12syn.f
in the
same directory as testigrf.c
.
Then, open a console in that directory and execute the following commands:
$ gcc -c -o testigrf.o testigrf.c
$ gfortran -fno-underscoring -c -o igrf12syn.o igrf12syn.f
$ gfortran -o testigrf testigrf.o igrf12syn.o
These will compile and link your program as testigrf
in the same directory.
Finally, repeat the first two commands, each with the additional option -Wall
,
to see the warnings that you are not yet aware of. Consider fixing the program
to remove the warnings, as they may well mean that the program will not
behave as you expect. Always include -Wall
in your gcc/g++/gfortran
compiler
options when compiling code of any importance to you.
Linking a Fortran program with an arbitrary binary file
As mentioned in the a comment, you want to use the iso_c_binding
intrinsic Fortran module to provide C interoperability features.
The following code/example is produced with GCC 4.9.2 on x86_64-pc-linux-gnu.
Scalar data
Consider the following Fortran:
module data
use iso_c_binding
implicit none
real(kind=c_double), bind(C) :: val
end module
program fbin
use data
implicit none
print *,val
end program
This uses a Fortran module containing the variable var
which is compatible with the C type double
. The bind(C)
attribute will cause the variable exposed in the object file to be unmangled (e.g. var
with no underscoring). Note the use of the module is necessary because you cannot apply the bind attribute in the main program scope (or at least GCC won't allow this, I haven't consulted the standard).
I used your code to produce data.bin, but for my system I had to modify the objcopy
as follows:
objcopy -I binary -O elf64-x86-64 -B i386 --redefine-sym _binary_data_bin_start=val data.bin data.o
The changes are the bfdname to produce an object my system can consume and changing the symbol name to plain val
to match what the Fortran compiler will expect.
The rest follows as your example:
% objdump -t data.o
data.o: file format elf64-little
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g .data 0000000000000000 val
0000000000000008 g .data 0000000000000000 _binary_data_bin_end
0000000000000008 g *ABS* 0000000000000000 _binary_data_bin_size
and
% gfortran -o fbin fbin.f90 data.o
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/../../../../x86_64-pc-linux-gnu/bin/ld: Warning: alignment 1 of symbol `val' in data.o is smaller than 8 in /tmp/ccYcttlP.o
Note that this warning does not cause the program to fail, but will impact the performance of the program. I'm not sure if you'll have alignment issues on your platform. Finally:
% ./fbin
3.1415926535897931
Success.
Array data
Here is a modified build.c to produce a 1-d array of values for testing:
#include <math.h>
#include <stdio.h>
int main() {
FILE *f;
int i;
double x;
f = fopen("data.bin", "wb");
for (i=0; i<15; i++) {
x = M_PI*i;
fwrite(&(x), sizeof x, 1, f);
}
fclose(f);
return 0;
}
This produces data.bin with 15 8-byte values from 0 to 14*M_PI. The data.o
is produced with the same command as in my scalar example.
Here is an example Fortran program that is hardcoded to load a rank 2 array with shape (3,5).
module data
use iso_c_binding
implicit none
integer, parameter :: n = 3
integer, parameter :: m = 5
real(kind=c_double), target, bind(C) :: val
real(kind=c_double), dimension(:,:), pointer :: array
contains
subroutine init_fort_array()
use iso_c_binding
implicit none
call c_f_pointer(c_loc(val), array, [n,m])
end subroutine
end module
program forbin
use data
implicit none
integer :: j
call init_fort_array()
print '(5(f8.5,2x))',(array(1:n,j), j=1,m)
end program
This is a little less straightforward and is perhaps not the only way to accomplish this, but it is the one that came to mind. The module has a scalar value val
and an array pointer. A subroutine gets the address of val with c_loc
and associates the Fortran pointer array
with that address and using the given shape. You just need to call that subroutine in your code and then array
works as expected.
This array is hardcoded but you could use other data files (or a single data file with multiple symbols) to pack the array rank and dimensions into the data object and then change the Fortran to use a shape dependent on the actual data.
No example would be complete without output:
% ./fbin
0.00000 9.42478 18.84956 28.27433 37.69911
3.14159 12.56637 21.99115 31.41593 40.84070
6.28319 15.70796 25.13274 34.55752 43.98230
Note that Fortran and C do not use the same array ordering. C is row-major and Fortran is column major and you can see this in how my Fortran loop prints the data.
Hybrid programming of Fortran and C++: Fortran can not call C++ subroutines
You need to link the executable against the c++ standard library:
g++ -c sub1.1.cxx
gfortran -o test sub2.f90 sub1.1.o -lstdc++
With gfortran/g++ you can also use g++ to link against the fortran library:
g++ -c sub1.1.cxx
gfortran -c sub2.f90
g++ -o test sub1.o sub2.o -lgfortran
Compile and link Fortran and C with ifort and icc
The -static
flag will link all the libraries statically. In that case you need to have a static version (the .a
files) of every library. For example, using -lm
will search for libm.a
. Those libraries are not installed by default, but may be in the -dev
or -devel
packages of your distribution.
If you only want to link statically the Intel libraries, then you should use -static-intel
.
A good trick to avoid static linking is to:
1) Dynamically link your program with -static-intel
and -Wl,-rpath=./lib
2) Use ldd
to find which libraries your program needs
3) Create a directory lib
where you copy all the required dynamic libraries
4) Instead of distributing your code as a single static binary you can disrtibute it as a binary + the lib directory (assuming the licenses of the libraries permit it).
Finally, if you need to try more things, I have succeeded to link an Intel Fortran file with gcc using this command:
$ gcc fortran_file.o c_main_file.o -lifcore -lirc -lcomposerxe_gen_helpers_core_2.3
hope this helps...
Mixed C++ and Fortran Linking Issue
There are few issues here that don't let names of the objects match. First, specify in the C++ code that the external functions have the C signature:
In test.cpp:
extern "C" int Add( int *, int * );
extern "C" int Multiply( int *, int * );
See In C++ source, what is the effect of extern "C"? for more details.
In your Fortran code, make the interface explicit by placing procedures in the module, and use iso_c_binding
to let Fortran objects appear as valid C objects. Notice that we can explicitly specify the names of the objects that the C or C++ programs will see through the bind
keyword:
test_f.f90:
module mymod
use iso_c_binding
implicit none
contains
integer(kind=c_int) function Add(a,b) bind(c,name='Add')
integer(kind=c_int) :: a,b
Add = a+b
end function
integer(kind=c_int) function Multiply(a,b) bind(c,name='Multiply')
integer(kind=c_int) :: a,b
Multiply = a*b
end function
endmodule mymod
Compile (don't mind me using the Intel suite, my g++ & gfortran are very old):
$ ifort -c test_f.f90
$ icpc -c test.cpp
Link:
$ icpc test_f.o test.o
Executing a.out
should now work as expected.
Related Topics
Sending an Email from a C/C++ Program in Linux
Single Process Maximum Possible Memory in X64 Linux
Make Shared_Ptr Not Use Delete
How to Make Std::Vector's Operator[] Compile Doing Bounds Checking in Debug But Not in Release
Drawing Sphere in Opengl Without Using Glusphere()
How Do Sizeof(Arr)/Sizeof(Arr[0]) Work
Why Shared_From_This Can't Be Used in Constructor from Technical Standpoint
How to Convert Std::Chrono::Time_Point to Calendar Datetime String with Fractional Seconds
Determine If Linux or Windows in C++
Press Anykey to Continue in Linux C++
How to Enable C++11 in Qt Creator
Selecting a Member Function Using Different Enable_If Conditions
How to Initialize Std::Array<T, N> Elegantly If T Is Not Default Constructible
How to Read/Write Arbitrary Bits in C/C++
Is the Order of Iterating Through Std::Map Known (And Guaranteed by the Standard)