minimum modification to make a visual studio FORTRAN-C mixed code gfortran-gcc compatible

253 Views Asked by At

I'm trying to modify this code (gist as back up) to become gfortran-gcc compatible.

  • I removed the [VALUE] tags
  • used POINTER with -fcray-pointer flag for gfortran, instead of the [REFERENCE] tag
  • removed the __stdcall , because tried the #define __stdcall __attribute__((stdcall)) which caused warning: ‘stdcall’ attribute ignored [-Wattributes] now this is what I have:

C code CMAIN.C:

#include <stdio.h>

extern int FACT_(int n);
extern void PYTHAGORAS_(float a, float b, float *c);

main()
{
    float c;
    printf("Factorial of 7 is: %d\n", FACT_(7));
    PYTHAGORAS_(30, 40, &c);
    printf("Hypotenuse if sides 30, 40 is: %f\n", c);
}

the FORTRAN code FORSUBS.FOR:

  INTEGER*4 FUNCTION Fact (n)
  INTEGER*4 n 
  INTEGER*4 i, amt
  amt = 1
  DO i = 1, n
    amt = amt * i
  END DO
  Fact = amt
  END

  SUBROUTINE Pythagoras (a, b, cp)
  REAL*4 a
  REAL*4 b
  POINTER (cp, c)
  REAL*4 c
  c = SQRT (a * a + b * b)
  END

the Makefile:

all:
    gfortran -c FORSUBS.FOR -fcray-pointer
    gcc -c CMAIN.C
    gfortran -o result.out FORSUBS.o CMAIN.o  
    rm -rf *.o

clean :
    rm -rf *.out *~ *.bak *.o

However I still get the error:

CMAIN.o: In function `main':

CMAIN.C:(.text+0x1d): undefined reference to `FACT_(int)'

CMAIN.C:(.text+0x4c): undefined reference to `PYTHAGORAS_(float, float, float*)'

I would appreciate if you could help me know:

  • What is the issue and how can I resolve it?
  • what is the best method to modify the original code to become gcc-gfortran compatible with minimum change.

P.S.1. also shared on Reddit. P.S.2. operating system and compiler specifications are same as this question.

2

There are 2 best solutions below

4
On BEST ANSWER

In addition to my top comments, Fortran passes by reference, so you have to modify the .c and the .for files.

The code below works. There may be a simpler way to declare things, but this should [at least] get you further along. Caveat: I haven't done much fortran since Fortran IV days, so I'm a bit rusty. I'd defer to Vladimir's VALUE solution as a better way to go.


#include <stdio.h>

#if 0
extern int fact_(int n);
extern void pythagoras_(float a, float b, float *c);
#else
extern int fact_(int *n);
extern void pythagoras_(float *a, float *b, float *c);
#endif

int
main(void)
{
    float c;
#if 0
    printf("Factorial of 7 is: %d\n", fact_(7));
#else
    int n = 7;
    printf("Factorial of 7 is: %d\n", fact_(&n));
#endif

#if 0
    pythagoras_(30, 40, &c);
#else
    float a = 30;
    float b = 40;
    pythagoras_(&a, &b, &c);
#endif
    printf("Hypotenuse if sides 30, 40 is: %f\n", c);

    return 0;
}

    INTEGER*4 FUNCTION Fact (n)
    INTEGER*4 n
    INTEGER*4 i, amt
    amt = 1
    DO i = 1, n
        amt = amt * i
    END DO
    Fact = amt
    END

    SUBROUTINE Pythagoras (a, b, c)
    REAL*4 a
    REAL*4 b
    REAL*4 c
    c = SQRT (a * a + b * b)
    END

Here is the program output:

Factorial of 7 is: 5040
Hypotenuse if sides 30, 40 is: 50.000000

UPDATE:

I get the same undefined reference error from your code!

Aha!

One thing I didn't mention [because I didn't think it would make a difference] is that I changed the source file names to use all lowercase letters (e.g. CMAIN.C --> cmain.c and FORSUBS.FOR --> forsubs.for)

With that, the output of nm *.o produces:

 cmain.o:
                 U fact_
0000000000000000 T main
                 U printf
                 U pythagoras_

forsubs.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_

The change in the fortran source filename doesn't matter too much. But, the C source filename does!

More to the point it's the filename suffix (i.e. .C changed to .c).

This is because gcc will [try to be smart and] look at the suffix to determine which language the file is written in and compile accordingly. For example, gcc -c foo.cpp will compile the file as if it is written in c++ and not c, just as if the command were: g++ -c foo.cpp

Although .cpp is the [more] usual suffix for a c++ filename, an alternate suffix for c++ files is: .C

That is, most projects use the .cpp convention, but some use the .C convention. One of the reasons for preferring .cpp over .C is that Windows filesystems are case insensitive. So, .C and .c would appear the same. However, POSIX systems (e.g. linux, macOSX, iOS, android, etc.) have case sensitive filenames so either convention can be used.

So, gcc -c CMAIN.C will compile as c++. This does c++ style "name mangling" of symbols--not what we want. In c++, the mangling is done to allow "overloading" of function names. That is, two [or more] different functions can have the same name, as long as they use different arguments. For example:

void calc(int val);
void calc(int val1,int val2);
void calc(double fval);

Here is the output of nm *.o if we use CMAIN.C:

 CMAIN.o:
0000000000000000 T main
                 U printf
                 U _Z11pythagoras_PfS_S_
                 U _Z5fact_Pi

FORSUBS.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_

Running that file through c++filt to "demangle" the c++ names we get:

 CMAIN.o:
0000000000000000 T main
                 U printf
                 U pythagoras_(float*, float*, float*)
                 U fact_(int*)

FORSUBS.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_

So, try to use lowercase filenames if possible [that is recommended best practice]. But, at a minimum, don't use .C

6
On

Fortran passes variables by reference, as mentioned in the first sentences of any Fortran to C tutorial. So you:

  1. Cannot just remove the [VALUE], you should add the modern VALUE attribute or change the C code to pass pointers.

  2. Should not use pointers on the Fortran side of [REFERENCE], but just remove it.

  3. Use the actual name mangling for your compiler, these days normally subroutine_name_ (appended underscore, name is lowercase) or use the modern bind(C, name="binding_name") attribute.

Without modern Fortran, but with VALUE (it is just a PITA without):

  INTEGER*4 FUNCTION Fact (n)
  INTEGER*4, VALUE :: n 
  INTEGER*4 i, amt
  amt = 1
  DO i = 1, n
    amt = amt * i
  END DO
  Fact = amt
  END

  SUBROUTINE Pythagoras (a, b, c) bind(C)
  REAL*4, VALUE :: a
  REAL*4, VALUE :: b
  REAL*4 c
  c = SQRT (a * a + b * b)
  END

and than just change your C names to lowercase (pythagoras_, fact_)... With the VALUE attribute you do not need to introduce all those C temporaries you see in the other answer. The bind(C) is required for VALUE to work properly. It will save you from rewriting the code that calls the Fortran procedures.

For best modern experience use bind(C,name="any_name_you_want") to set the exact linkage symbol name.