I am compiling a C++ code together with Fortran subroutine. The C++ cpp code is like:
#include "Calculate.h"
extern "C" double SolveEq_(double *Gvalue, double *GvalueU, double *GvalueV, double *Gnodex, double *Gnodey, double *GtimeInc, double *Glfs);
template <class T1, class T2>
void Calculate(vector<Element<T1, T2> > &elm, GParameter &GeqPm, GmeshInfo &Gmesh)
{
// Solving Equation using Fortran code
SolveEq_(&Gmesh.Gvalue[0], &Gmesh.GvalueU[0], &Gmesh.GvalueV[0], &Gmesh.Gnodex[0], &Gmesh.Gnodey[0], &GeqPm.GtimeInc, &GeqPm.Glfs);
return;
}
And the Fortran code is like:
!==========================================================================
Module Inputpar
Implicit None
Integer,parameter :: Numx = 200, Numy = 200
End Module
!======================================== PROGRAM =============================================
Subroutine SolveEq(Gvalue, GvalueU, GvalueV, Gnodex, Gnodey, Deltat, Lfs);
Use Inputpar
Implicit None
Real*8 Deltat, Lfs, Dt, Su
Real*8 Gvalue(1, (Numx+1)*(Numy+1)), GvalueU(1, (Numx+1)*(Numy+1)), GvalueV(1, (Numx+1)*(Numy+1))
Real*8 Gnodex(0:Numx), Gnodey(0:Numy)
Real*8 DX, DY
Real*8 X(-3:Numx+3), Y(-3:Numy+3)
Real*8 VelX(-3:Numx+3,-3:Numy+3), VelY(-3:Numx+3,-3:Numy+3)
Real*8 G(-3:Numx+3,-3:Numy+3)
Common /CommonData/ X, Y, DX, DY, VelX, VelY, G, Dt, Su
!============================= Data Transfer ============================
Dt = Deltat
Su = Lfs
X (0:Numx) = Gnodex(0:Numx)
Y (0:Numy) = Gnodey(0:Numy)
VelX(0:Numx,0:Numy) = transpose(reshape(GvalueU,(/Numy+1,Numx+1/)))
VelY(0:Numx,0:Numy) = transpose(reshape(GvalueV,(/Numy+1,Numx+1/)))
G (0:Numx,0:Numy) = transpose(reshape(Gvalue ,(/Numy+1,Numx+1/)))
!==========Some other lines neglected here=================
End
!======================================== END PROGRAM =========================================
Firstly compile the Fortran code using command:
gfortran SolveEq.f90 -c -o SolveEq.o
And then compile the C++/Fortran codes together using makefile:
# Compiler
CC = g++
# Debug option
DEBUG = false
# Source directory of codes
SRC1 = /home
SRC2 = $(SRC1)/Resources
SRC3 = $(SRC1)/Resources/Classes
OPT=-fopenmp -O2
ifdef $(DEBUG)
PROG=test.out
else
PROG=i.out
endif
# Linker
#LNK=-I$(MPI)/include -L$(MPI)/lib -lmpich -lopa -lmpl -lpthread
OBJS = libtseutil.a Calculate.o SolveEq.o
OBJS_F=$(OBJS)
SUF_OPTS1=$(OBJS_F)
SUF_OPTS2=-I$(SRC2)/
SUF_OPTS3=-I$(SRC3)/
SUF_OPTS4=
# Details of compiling
$(PROG): $(OBJS_F)
$(CC) $(OPT) -o $@ $(SUF_OPTS1)
%.o: $(SRC1)/%.cpp
$(CC) $(OPT) -c $< $(SUF_OPTS2)
%.o: $(SRC2)/%.cpp
$(CC) $(OPT) -c $< $(SUF_OPTS3)
%.o: $(SRC3)/%.cpp
$(CC) $(OPT) -c $< $(SUF_OPTS4)
# Clean
.PHONY: clean
clean:
@rm -rf *.o *.oo *.log
However, the error shows that:
g++ -fopenmp -O2 -o libtseutil.a Calculate.o SolveEq.o
Calculate.o: In function `void Calculate<CE_Tri, SolElm2d>(std::vector<Element<CE_Tri, SolElm2d>, std::allocator<Element<CE_Tri, SolElm2d> > >&, GParameter&, GmeshInfo&)':
Calculate.cpp:(.text._Z10CalculateGI6CE_Tri8SolElm2dEvRSt6vectorI7ElementIT_T0_ESaIS6_EER10GParameterR9GmeshInfo[_Z10CalculateGI6CE_Tri8SolElm2dEvRSt6vectorI7ElementIT_T0_ESaIS6_EER10GParameterR9GmeshInfo]+0x3c): undefined reference to `SolveEq_'
SolveEq.o: In function `solveeq_':
SolveEq.f90:(.text+0x2b8e): undefined reference to `_gfortran_reshape_r8'
SolveEq.f90:(.text+0x2d2a): undefined reference to `_gfortran_reshape_r8'
SolveEq.f90:(.text+0x2ec6): undefined reference to `_gfortran_reshape_r8'
SolveEq.f90:(.text+0x31fa): undefined reference to `_gfortran_reshape_r8'
collect2: error: ld returned 1 exit status
How does this happen?
I used a simple case to test the mixed compiling. The C++ code was:
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <vector>
using namespace std;
extern "C" double fortran_sum_(double *sum, double *su2m, double *vec, double* vec2, int *size);
int main(int argc, char ** argv)
{
int size;
double sum, sum2;
double aa,bb,cc,dd;
vector<double> vec;
vector<double> vec2;
size=2;
aa=1.0;
bb=2.0;
sum=0.0;
sum2=0.0;
vec.push_back(aa);
vec.push_back(bb);
vec2.push_back(aa*2.0);
vec2.push_back(bb*2.0);
fortran_sum_(&sum, &sum2, &vec[0], &vec2[0], &size);
cout << "Calling a Fortran function" << endl;
cout << "============================" << endl;
cout << "size = " << size << endl;
cout << "sum = " << sum << endl;
cout << "sum2 = " << sum2 << endl << endl;
}
And the Fortran code was:
Subroutine fortran_sum(gsum, gsum2, gvec, gvec2, gsize2)
integer gsize,gsize2
real*8 gvec(0:gsize2-1), gvec2(0:1)
real*8 gsum, gsum2
gsum = gvec(0)+gvec(1);
gsum2 = gvec2(0)+gvec2(1);
gsize = gsize*2;
end
Then used commands to compile :
gfortran fortran_sum.f90 -c -o fortran_sum.o
g++ fortran_sum.o call_fortran.cpp -o a.out
./a.out
It worked well:
Calling a Fortran function
============================
size = 2
sum = 3
sum2 = 6
I am little bit weak on Fortran language. When I compiled your fortran code and put it through nm, it gave me the following. There is no symbol "SolveEq_". There is just "solveeq_".
Edit: It compiled for me when I used "solveeq_". Here is simplified code for demo (main.cpp):
Compiled it as (f.f90 has the fortran code):
It seems, since 2003, if you want to call a fortran function from C/C++, you could use BIND (It may not with fortran/fortran though without some more additional effort).
Thanks to every one, especially @Brick and @blackpen. The problem has been fixed.
1), Add -lgfortran into command line so that the function,otherwise it will show: undefined reference to `_gfortran_reshape_r8'.
2), Change the name of the .f90 function into small letters "solveeq" other wise it will show: undefined reference to `SolveGeq_'
So finally my .cpp is changed into :
The fortran code .f90 is like:
And the makefile is like:
The function
_gfortran_reshape_r8
is part of the library that is used by code compiled bygfortran
. When you compile in a single language such libraries are automatically linked because whichever program you use to do the linking knows about them. When you link mixed code, you need to find and explicitly put on the command line the libraries for the language that doesn't correspond to the linker you've chosen. Usually you link with the C++ syntax, as you've done here, and add the fortran compiler's libraries explicitly.