James Matta
James Matta

Reputation: 1570

Fortran functions with C interface result in undefined references when they try to call each other, why?

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?

Upvotes: 1

Views: 1576

Answers (2)

francescalus
francescalus

Reputation: 32451

[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.

Upvotes: 2

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.

Upvotes: 2

Related Questions