3

I am a newcomer to this, but I need to access some old Fortran 77 functions from C. I don't want to alter the Fortran code if possible, I would really prefer to write a wrapper to call the Fortran functions from C. I hope to get a minimal working example (on Linux). What I did:

In file somefunction.f:

REAL*8 FUNCTION MYFUNC(ZZ)
      IMPLICIT NONE
      REAL*8 ZZ, T1
      T1 = ZZ + 1.0D0
      MYFUNC = T1
      RETURN
      END

compiled with gfortran -c somefunction.f -o somefunction.o.

In file debug.c:

#include <stdio.h>

double cfunc(double x) {
    double result = myfunc_( &x );
    return result;
}
int main() {
    double test = cfunc(3.0);
    printf(" %.15f ",test);
}

compiled with gcc -c debug.c -o debug.o.

Then I give gcc debug.o somefunction.o and ./a.out.

However, instead of 3+1=4, I get nonsensical numbers. How do I correct this?


P.S: If this is solved, the actual functions I have are a bit more complicated:

  1. What should I change if MYFUNC were to be instead of the type COMPLEX*16 FUNCTION MYFUNC(ZZ) with ZZ also a complex?

  2. What if MYFUNC called some built-in Fortran function, say CDLOG(ZZ) ?

  3. And what if it accesses a common block? Can this be accommodated too?

Marco
  • 79
  • 2
  • 1
    Do you declare the return type of myfunc anywhere in the C? – Ian Bush Mar 18 '20 at 22:57
  • You are right, that was the problem. Thanks! Any hint on the other points is also appreciated! – Marco Mar 18 '20 at 23:02
  • Quite possibly - I'm not an expert here, C is very much my second language, but when I learnt it all functions were assumed to return int unless you said otherwise. Your Fortran function is returning the (completely non-Standard) Real*8 - at the very least please use Double Precision rather than this. – Ian Bush Mar 18 '20 at 23:08
  • @IanBush IIRC, `real*8` is fortran's way of specifying `double`. It is [loosely] "float that occupies 8 bytes". And, in fortran, `real*8` has been around for decades ... – Craig Estey Mar 19 '20 at 00:22
  • See: https://northstar-www.dartmouth.edu/doc/solaris-forte/manuals/fortran/prog_guide/11_cfort.html – Craig Estey Mar 19 '20 at 00:27
  • 2
    @CraigEstey No it isn't. Real*8 is not and has never been part of standard Fortran and as such its behaviour is implementation defined, and there have been questions asked with compilers that don't support it. Rather the standard defined kind mechanism should be used. See https://stackoverflow.com/questions/838310/fortran-90-kind-parameter Also the best way to answer this would be Fortran's standard defined way to Interface with C. – Ian Bush Mar 19 '20 at 00:35
  • 1
    @IanBush I didn't say it was a standard, just that it's been around for decades. fortran is a trailing edge language. The only reason to write new code is to interface with legacy scientific libraries [which probably have millions of lines of code that use `real*8`]. Use of `real*8` predates `kind` by at least two decades. So, every credible fortran compiler that needs to be relevant will support `real*8`. And, `real*8 xs` is more compact than `REAL(KIND=8) :: XS`, so it wins due to the brevity of something that is needed/used everywhere. And, F2008 allows `REAL64` which is _still_ better. – Craig Estey Mar 19 '20 at 01:41
  • @CraigEstey `REAL(KIND=8)` is even worse! see other discussions on stack overflow about the `kind` parameter, the value is completely non standard. – albert Mar 19 '20 at 10:14
  • 3
    You've tagged with fortran77, but if it is an option to write a wrapper on the fortran side, I strongly recommend using the `iso_c_binding` module provided by FORTRAN2003: a) It defines datatypes that are guaranteed to match with their C counterparts (= no guessing whether `real*8` really is a `double`, etc.), and b) a `bind(c, name="foo")` function has a clearly defined signature and symbol name in C (`foo(...)` is guaranteed to work as expected). In short, it takes you out of the muddy implementation defined behavior area into the well defined territory of standard conforming C/fortran. – cmaster - reinstate monica Mar 20 '20 at 09:14
  • Which compiler on what platform? Are any parameters passed through the common blocks? Yolu will only need to know about named/unnamed common blocks if parameters are passed through them. For complex*16, you need to declare a structure with two floats/doubles. You'll have to experiment which is real/imaginary. – cup Mar 29 '20 at 07:40

2 Answers2

3

In addition to the return type, you also need to pay attention to the way the Fortran procedure name is mangled into a symbol that you access from C. There's no standard, it entirely depends on the Fortran compiler and platform, so you'll have to use some configure-time detection and a macro if you want portability.

For example, Fortran MYFUNC() needs to be called from C as:

  • MYFUNC() under Intel Fortran on Windows
  • myfunc() under IBM XL Fortran (xlf)
  • myfunc_() in most other cases (including GNU Fortran on Windows)

(It gets even more complicated when the Fortran routine is in a module, because then the mangled name is prefixed by the module and _MOD_, _mp_, etc.)

The modern Fortran solution is to declare a bind(C) routine on the Fortran side, which turns this mangling off (or you can specify an explicit binding name).

TooTea
  • 171
  • 6
2

OK, just for reference, as @IanBush said, the C program should have declared the return type of myfunc,

#include <stdio.h>

double myfunc_(double*);

double cfunc(double x) {
    double result = myfunc_( &x );
    return result;
}
int main() {
     double test = cfunc(3.0);
     printf(" %.15f ",test);
}
Marco
  • 79
  • 2