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
The function
_gfortran_reshape_r8is 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.