문제

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?

도움이 되었습니까?

해결책

_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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top