Question

I have an old fortran code, for various reasons I am modifying it to provide functionality using c++.

In this code there are two functions, CALC and CALC2. These functions need to be called from the c++ and they call each other.

A sketch of CALC:

SUBROUTINE CALC(W,NW,DW,IDMX,JUMP,POINT) bind(C, name="CALC")
    use iso_c_binding
C   Some statements
    IF (LO(36)) CALL CALC2(W,NW,DW,IDMX, .TRUE., 14)
C   More statements
END

A sketch of CALC2:

SUBROUTINE CALC2(W,NW,DW,IDMX,JUMP,POINT) bind(C, name="CALC2")
    use iso_c_binding
C   Some statements
    IF (LO(222)) CALL CALC(W,NW,DW,IDMX, .TRUE., 2)
C   More statements
END

My current main.cpp:

extern "C" {
void CALC(double *w, double *nw, double *dw, int *idmx, int* jump, int* point);
void CALC2(double *w, double *nw, double *dw, int *idmx, int* jump, int* point);
}

int main()
{
    int length = 600000;
    int jump = 0;
    int point = 0;
    double *w = new double[length];
    CALC( w, w, w, &length, &jump, &point);
    CALC2( w, w, w, &length, &jump, &point);
    return 0;
}

Running my make file everything compiles properly, but come the link phase I get this:

g++ (big list of .o files) -lgfortran -o ecis
b1_calc.o: In function `CALC':
b1_calc.f:(.text+0x16b): undefined reference to `calc2_'
bq_calc.o: In function `CALC2':
bq_calc.f:(.text+0x1e52): undefined reference to `calc_'
bq_calc.f:(.text+0x1ec0): undefined reference to `calc_'
collect2: error: ld returned 1 exit status
make: *** [ecis] Error 1

Why is this the case and how do I fix it?

Was it helpful?

Solution

[Edit, thanks to Vladimir F and eriktous for pointing out the standard requires the interface; and thus pointing out that my original (untested) solution wasn't quite right.]

The binding label (the name= part of bind(c)) is an identifier for the C processor, so in particular it is case sensitive and won't have gfortran-style decorations in the final symbol). However, without an interface available to it, gfortran will in CALC2 create a reference to the lower case symbol calc_ (as given in the linker output).

So, you need to let gfortran/linker know that the correct symbols. You do this by providing an interface. Also, note that the standard requires an explicit interface when bind(c) is used - but you could get this "to work", even though not standard compliant.

An easy way to create an explicit interface would be to use modules: put the subroutines in one file with module calcs/end module calcs at the top/bottom. Or:

subroutine calc(...)
  ...
  interface
    subroutine calc2(...) bind(c, name='CALC2')
      ...
    end subroutine
  end interface
  call CALC2(...)
end subroutine

Also, having interfaces is good, anyway, as it enables a lot of checking.

Alternatively, change the binding labels to lower case and append an underscore. This may well work, but is outside the standard and implementation-specific. Having the functions in separate files is thwarting the compiler's chances to detect the error.

OTHER TIPS

You must provide explicit interface to the functions when calling them from Fortran. And you are calling them from each other (in Fortran). Place them in a module or use interface blocks.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top