Reputation: 379
I am in the processes of converting a program from Fortran to C++. To maintain input compatibility, however, I am using subroutines and Fortran's namelist functionality following the instructions in this question. Since this is a legacy code, the namelist I am working on is a bit more ridiculous with 30+ variables. Everything works up until linking when I get the error:
main.cpp:(.text+0x2732): undefined reference to `readDatainMesh(double*, int*, int*, double*, char*, double*, char*, int*, int*, int*, double*, int*, int*, int*, double*, double*, double*, int*, int*, int*, int*, double*, double*, double*, double*, int*, int*, double*, int*, int*, double*, char*, double*, double*, double*, int*, double*, double*, double*, int*, int*, int*, char*, char*, double*, double*)'
I have the following input files:
readNamelists.f90:
subroutine readDatainMesh(...) &
bind(c, name='readDatainMesh')
use,intrinsic :: iso_c_binding,only:c_double,c_int,c_char
implicit none
real(kind=c_double), intent(inout) :: realVars
integer(kind=c_int), intent(inout) :: intVars
character(kind=c_char), intent(inout) :: charVars
.
.
.
namelist/datain_mesh/...
open(unit = 100, file = 'input.nam', status = 'old')
read(unit = 100, nml = datain_mesh)
close(unit = 100)
endsubroutine readDatainMesh
which I compile with gfortran -Wall -o readNamelists.o -c readNamelists.f90
. This produces warnings about unused dummy variables, but that is all.
I have a C header for the function, which I have checked against both the called function in main.cpp and the fortran implementation. It looks like:
#ifndef READNAMELISTS_H
#define READNAMELISTS_H
void readDatainMesh(...);
#endif
I call gcc -Wall -o main.o -c main.cpp
which gives no warnings. Then finally gcc -Wall -o main main.o readNamelists.o -lstdc++ -lgfortran
which throws the above error. I thought that perhaps the symbols werent matching so I ran nm
on both object files and the lines of interest are:
0000000000000000 T readDatainMesh
and
U _Z14readDatainMeshPdPiS0_S_PcS_S1_S0_S0_S0_S_S0_S0_S0_S_S_S_S0_S0_S0_S0_S_S_S_S_S0_S0_S_S0_S0_S_S1_S_S_S_S0_S_S_S_S0_S0_S0_S1_S1_S_S_
which obviously don't match. My question is how to I resolve this?
Upvotes: 0
Views: 97
Reputation: 15989
_Z14readDatainMeshPdPiS0_S_PcS_S1_S0_S0_S0_S_S0_S0_S0_S_S_S_S0_S0_S0_S0_S_S_S_S_S0_S0_S_S0_S0_S_S1_S_S_S_S0_S_S_S_S0_S0_S0_S1_S1_S_S_
is the C++ mangled name including the parameter types. This way the C++ compiler can give overloaded functions different symbols and distinguish them.
One can demangle it from command line:
$ echo _Z14readDatainMeshPdPiS0_S_PcS_S1_S0_S0_S0_S_S0_S0_S0_S_S_S_S0_S0_S0_S0_S_S_S_S_S0_S0_S_S0_S0_S_S1_S_S_S_S0_S_S_S_S0_S0_S0_S1_S1_S_S_ | demangle
readDatainMesh(double*, int*, int*, double*, char*, double*, char*, int*, int*, int*, double*, int*, int*, int*, double*, double*, double*, int*, int*, int*, int*, double*, double*, double*, double*, int*, int*, double*, int*, int*, double*, char*, double*, double*, double*, int*, double*, double*, double*, int*, int*, int*, char*, char*, double*, double*)
You therefore have to tell the C++ compiler to treat this as C function without overloading. This can be done using extern "C"
. If the same header is used from C and C++code you also need a guard, as extern "C"
is not valid in C.
in full the header might look like this:
#ifndef READNAMELISTS_H
#define READNAMELISTS_H
#ifdef __cplusplus
extern "C" {
#endif
void readDatainMesh(...);
#ifdef __cplusplus
}
#endif
#endif
In case you do C++ only and that's the only function you can shorten it to
#ifndef READNAMELISTS_H
#define READNAMELISTS_H
extern "C" void readDatainMesh(...);
#endif
Upvotes: 2